1 /** 2 * Copyright 2005-2014 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 }