1 /**
2 * Copyright 2005-2011 The Kuali Foundation
3 *
4 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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.kuali.rice.core.api.util;
17
18 import org.apache.commons.lang.ArrayUtils;
19 import org.apache.commons.lang.builder.HashCodeBuilder;
20
21 import java.lang.reflect.Field;
22 import java.util.Calendar;
23
24 /**
25 * Class of static utility methods used to aid in the generation of hashcode values and equals comparisons of objects
26 * for corner cases that EqualsBuilder and HashCodeBuilder of commons-lang cannot cover.
27 */
28 public final class EqualsAndHashCodeUtils {
29
30 private EqualsAndHashCodeUtils(){
31 throw new UnsupportedOperationException("do not call");
32 }
33
34 /**
35 * This method provides an equals comparison of two objects by evaluating the results of compareTo across specified
36 * internal fields of the class of the two objects being compared.
37 * <p/>
38 * This method should be used where evaluating equality on fields of two instances of type T using .equals() yields
39 * false, but for the purposes of determining equality of the two instances of type T, should be true. An example
40 * is where a class has internal fields of type Calendar that need equality determined using only its time value
41 * and not other internal fields of Calendar.
42 *
43 * @param o1 The first object used in an equality operation using compareTo
44 * @param o2 The second object used in an equality operation using compareTo
45 * @param fieldNames All field names within type T that should be determined equal or not using compareTo
46 * @param <T> Type of both o1 and o2 parameters. Guarantees both o1 and o2 are the same reference type.
47 * @return true if (o1.field.compareTo(o2.field) == 0) is true for all passed in fieldNames. Otherwise false
48 * is returned. False is also returned if any fields specified in fieldNames are not of type Comparable or if one
49 * (but not both) of the passed in objects are null references.
50 */
51 public static <T> boolean equalsUsingCompareToOnFields(T o1, T o2, String... fieldNames) {
52 if (o1 == o2) {
53 return true;
54 }
55 if (o1 == null || o2 == null) {
56 return false;
57 }
58
59 boolean isEqual = true;
60 Class<?> targetClass = o1.getClass();
61 try {
62 for (String fieldName : fieldNames) {
63 Field field = targetClass.getDeclaredField(fieldName);
64 field.setAccessible(true);
65 Class<?> fieldClass = field.getType();
66
67 if (ArrayUtils.contains(fieldClass.getInterfaces(), Comparable.class)) {
68 @SuppressWarnings("unchecked") Comparable<Object> c1 = (Comparable<Object>) field.get(o1);
69 @SuppressWarnings("unchecked") Comparable<Object> c2 = (Comparable<Object>) field.get(o2);
70 if (c1 == c2) {
71 continue;
72 }
73 if (c1 == null || c2 == null) {
74 isEqual = false;
75 } else {
76 isEqual = (c1.compareTo(c2) == 0);
77 }
78 } else {
79 isEqual = false;
80 }
81
82 if (!isEqual) {
83 break;
84 }
85 }
86
87 return isEqual;
88 } catch (Exception e) {
89 throw new RuntimeException(e);
90 }
91 }
92
93 /**
94 * Generates an int hashcode from all calendars passed in. This is a convenience method for hashcode methods
95 * to call if they have to generate hashcodes from fields of type Calendar when those Calendar fields
96 * have equality evaluated using compareTo and not equals within the equals method of the container class.
97 *
98 * @param calendars
99 * @return int hashcode value generated by using the long value returned from each Calendar.getTimeInMillis()
100 */
101 public static int hashCodeForCalendars(Calendar... calendars) {
102 HashCodeBuilder hcb = new HashCodeBuilder();
103 for (Calendar calendar : calendars) {
104 if (calendar != null) {
105 hcb.append(calendar.getTimeInMillis());
106 }
107 }
108 return hcb.toHashCode();
109 }
110 }