View Javadoc
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 }