View Javadoc

1   /*
2    * Copyright 2004 Carlos Sanchez.
3    *
4    * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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 net.sf.oness.common.model.bo;
17  
18  import java.lang.reflect.AccessibleObject;
19  import java.lang.reflect.Field;
20  import java.lang.reflect.Modifier;
21  import java.util.Collection;
22  
23  import org.apache.commons.lang.builder.HashCodeBuilder;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  /***
28   * Customized EqualsBuilder ignoring fields that can't be accessed. Note that
29   * this behaviour can hide errors when testing.
30   * 
31   * Only append(Object, Object) was modified.
32   * 
33   * @author Carlos Sanchez
34   * @author Stephen Colebourne
35   * @author Gary Gregory
36   * @author Pete Gieser
37   * @version $Revision: 1.1 $
38   */
39  public class ExceptionSafeHashCodeBuilder extends HashCodeBuilder {
40  
41      private static Log log = LogFactory
42              .getLog(ExceptionSafeEqualsBuilder.class);
43  
44      /***
45       * Constant to use in building the hashCode.
46       */
47      private final int iConstant;
48  
49      /***
50       * Running total of the hashCode.
51       */
52      private int iTotal = 0;
53  
54      /***
55       * <p>
56       * Constructor.
57       * </p>
58       * 
59       * <p>
60       * This constructor uses two hard coded choices for the constants needed to
61       * build a <code>hashCode</code>.
62       * </p>
63       */
64      public ExceptionSafeHashCodeBuilder() {
65          super();
66          iConstant = 37;
67          iTotal = 17;
68      }
69  
70      /***
71       * <p>
72       * Constructor.
73       * </p>
74       * 
75       * <p>
76       * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
77       * these should be different for each class, however this is not vital.
78       * </p>
79       * 
80       * <p>
81       * Prime numbers are preferred, especially for the multiplier.
82       * </p>
83       * 
84       * @param initialNonZeroOddNumber
85       *            a non-zero, odd number used as the initial value
86       * @param multiplierNonZeroOddNumber
87       *            a non-zero, odd number used as the multiplier
88       * @throws IllegalArgumentException
89       *             if the number is zero or even
90       */
91      public ExceptionSafeHashCodeBuilder(int initialNonZeroOddNumber,
92              int multiplierNonZeroOddNumber) {
93          super();
94          if (initialNonZeroOddNumber == 0) {
95              throw new IllegalArgumentException(
96                      "HashCodeBuilder requires a non zero initial value");
97          }
98          if (initialNonZeroOddNumber % 2 == 0) {
99              throw new IllegalArgumentException(
100                     "HashCodeBuilder requires an odd initial value");
101         }
102         if (multiplierNonZeroOddNumber == 0) {
103             throw new IllegalArgumentException(
104                     "HashCodeBuilder requires a non zero multiplier");
105         }
106         if (multiplierNonZeroOddNumber % 2 == 0) {
107             throw new IllegalArgumentException(
108                     "HashCodeBuilder requires an odd multiplier");
109         }
110         iConstant = multiplierNonZeroOddNumber;
111         iTotal = initialNonZeroOddNumber;
112     }
113 
114     //-------------------------------------------------------------------------
115 
116     /***
117      * <p>
118      * This method uses reflection to build a valid hash code.
119      * </p>
120      * 
121      * <p>
122      * This constructor uses two hard coded choices for the constants needed to
123      * build a hash code.
124      * </p>
125      * 
126      * <p>
127      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
128      * private fields. This means that it will throw a security exception if run
129      * under a security manager, if the permissions are not set up correctly. It
130      * is also not as efficient as testing explicitly.
131      * </p>
132      * 
133      * <p>
134      * Transient members will be not be used, as they are likely derived fields,
135      * and not part of the value of the <code>Object</code>.
136      * </p>
137      * 
138      * <p>
139      * Static fields will not be tested. Superclass fields will be included.
140      * </p>
141      * 
142      * @param object
143      *            the Object to create a <code>hashCode</code> for
144      * @return int hash code
145      * @throws IllegalArgumentException
146      *             if the object is <code>null</code>
147      */
148     public static int reflectionHashCode(Object object) {
149         return reflectionHashCode(17, 37, object, false, null);
150     }
151 
152     /***
153      * <p>
154      * This method uses reflection to build a valid hash code.
155      * </p>
156      * 
157      * <p>
158      * This constructor uses two hard coded choices for the constants needed to
159      * build a hash code.
160      * </p>
161      * 
162      * <p>
163      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
164      * private fields. This means that it will throw a security exception if run
165      * under a security manager, if the permissions are not set up correctly. It
166      * is also not as efficient as testing explicitly.
167      * </p>
168      * 
169      * <P>
170      * If the TestTransients parameter is set to <code>true</code>, transient
171      * members will be tested, otherwise they are ignored, as they are likely
172      * derived fields, and not part of the value of the <code>Object</code>.
173      * </p>
174      * 
175      * <p>
176      * Static fields will not be tested. Superclass fields will be included.
177      * </p>
178      * 
179      * @param object
180      *            the Object to create a <code>hashCode</code> for
181      * @param testTransients
182      *            whether to include transient fields
183      * @return int hash code
184      * @throws IllegalArgumentException
185      *             if the object is <code>null</code>
186      */
187     public static int reflectionHashCode(Object object, boolean testTransients) {
188         return reflectionHashCode(17, 37, object, testTransients, null);
189     }
190 
191     /***
192      * <p>
193      * This method uses reflection to build a valid hash code.
194      * </p>
195      * 
196      * <p>
197      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
198      * private fields. This means that it will throw a security exception if run
199      * under a security manager, if the permissions are not set up correctly. It
200      * is also not as efficient as testing explicitly.
201      * </p>
202      * 
203      * <p>
204      * Transient members will be not be used, as they are likely derived fields,
205      * and not part of the value of the <code>Object</code>.
206      * </p>
207      * 
208      * <p>
209      * Static fields will not be tested. Superclass fields will be included.
210      * </p>
211      * 
212      * <p>
213      * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
214      * these should be different for each class, however this is not vital.
215      * Prime numbers are preferred, especially for the multiplier.
216      * </p>
217      * 
218      * @param initialNonZeroOddNumber
219      *            a non-zero, odd number used as the initial value
220      * @param multiplierNonZeroOddNumber
221      *            a non-zero, odd number used as the multiplier
222      * @param object
223      *            the Object to create a <code>hashCode</code> for
224      * @return int hash code
225      * @throws IllegalArgumentException
226      *             if the Object is <code>null</code>
227      * @throws IllegalArgumentException
228      *             if the number is zero or even
229      */
230     public static int reflectionHashCode(int initialNonZeroOddNumber,
231             int multiplierNonZeroOddNumber, Object object) {
232         return reflectionHashCode(initialNonZeroOddNumber,
233                 multiplierNonZeroOddNumber, object, false, null);
234     }
235 
236     /***
237      * <p>
238      * This method uses reflection to build a valid hash code.
239      * </p>
240      * 
241      * <p>
242      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
243      * private fields. This means that it will throw a security exception if run
244      * under a security manager, if the permissions are not set up correctly. It
245      * is also not as efficient as testing explicitly.
246      * </p>
247      * 
248      * <p>
249      * If the TestTransients parameter is set to <code>true</code>, transient
250      * members will be tested, otherwise they are ignored, as they are likely
251      * derived fields, and not part of the value of the <code>Object</code>.
252      * </p>
253      * 
254      * <p>
255      * Static fields will not be tested. Superclass fields will be included.
256      * </p>
257      * 
258      * <p>
259      * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
260      * these should be different for each class, however this is not vital.
261      * Prime numbers are preferred, especially for the multiplier.
262      * </p>
263      * 
264      * @param initialNonZeroOddNumber
265      *            a non-zero, odd number used as the initial value
266      * @param multiplierNonZeroOddNumber
267      *            a non-zero, odd number used as the multiplier
268      * @param object
269      *            the Object to create a <code>hashCode</code> for
270      * @param testTransients
271      *            whether to include transient fields
272      * @return int hash code
273      * @throws IllegalArgumentException
274      *             if the Object is <code>null</code>
275      * @throws IllegalArgumentException
276      *             if the number is zero or even
277      */
278     public static int reflectionHashCode(int initialNonZeroOddNumber,
279             int multiplierNonZeroOddNumber, Object object,
280             boolean testTransients) {
281         return reflectionHashCode(initialNonZeroOddNumber,
282                 multiplierNonZeroOddNumber, object, testTransients, null);
283     }
284 
285     /***
286      * <p>
287      * This method uses reflection to build a valid hash code.
288      * </p>
289      * 
290      * <p>
291      * It uses <code>AccessibleObject.setAccessible</code> to gain access to
292      * private fields. This means that it will throw a security exception if run
293      * under a security manager, if the permissions are not set up correctly. It
294      * is also not as efficient as testing explicitly.
295      * </p>
296      * 
297      * <p>
298      * If the TestTransients parameter is set to <code>true</code>, transient
299      * members will be tested, otherwise they are ignored, as they are likely
300      * derived fields, and not part of the value of the <code>Object</code>.
301      * </p>
302      * 
303      * <p>
304      * Static fields will not be included. Superclass fields will be included up
305      * to and including the specified superclass. A null superclass is treated
306      * as java.lang.Object.
307      * </p>
308      * 
309      * <p>
310      * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
311      * these should be different for each class, however this is not vital.
312      * Prime numbers are preferred, especially for the multiplier.
313      * </p>
314      * 
315      * @param initialNonZeroOddNumber
316      *            a non-zero, odd number used as the initial value
317      * @param multiplierNonZeroOddNumber
318      *            a non-zero, odd number used as the multiplier
319      * @param object
320      *            the Object to create a <code>hashCode</code> for
321      * @param testTransients
322      *            whether to include transient fields
323      * @param reflectUpToClass
324      *            the superclass to reflect up to (inclusive), may be
325      *            <code>null</code>
326      * @return int hash code
327      * @throws IllegalArgumentException
328      *             if the Object is <code>null</code>
329      * @throws IllegalArgumentException
330      *             if the number is zero or even
331      * @since 2.0
332      */
333     public static int reflectionHashCode(int initialNonZeroOddNumber,
334             int multiplierNonZeroOddNumber, Object object,
335             boolean testTransients, Class reflectUpToClass) {
336 
337         if (object == null) {
338             throw new IllegalArgumentException(
339                     "The object to build a hash code for must not be null");
340         }
341         HashCodeBuilder builder = new ExceptionSafeHashCodeBuilder(
342                 initialNonZeroOddNumber, multiplierNonZeroOddNumber);
343         Class clazz = object.getClass();
344         reflectionAppend(object, clazz, builder, testTransients);
345         while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
346             clazz = clazz.getSuperclass();
347             reflectionAppend(object, clazz, builder, testTransients);
348         }
349         return builder.toHashCode();
350     }
351 
352     /***
353      * <p>
354      * Appends the fields and values defined by the given object of the given
355      * <code>Class</code>.
356      * </p>
357      * 
358      * @param object
359      *            the object to append details of
360      * @param clazz
361      *            the class to append details of
362      * @param builder
363      *            the builder to append to
364      * @param useTransients
365      *            whether to use transient fields
366      */
367     private static void reflectionAppend(Object object, Class clazz,
368             HashCodeBuilder builder, boolean useTransients) {
369         Field[] fields = clazz.getDeclaredFields();
370         AccessibleObject.setAccessible(fields, true);
371         for (int i = 0; i < fields.length; i++) {
372             Field f = fields[i];
373             if ((f.getName().indexOf('$') == -1)
374                     && (useTransients || !Modifier
375                             .isTransient(f.getModifiers()))
376                     && (!Modifier.isStatic(f.getModifiers()))) {
377                 try {
378                     builder.append(f.get(object));
379                 } catch (IllegalAccessException e) {
380                     //this can't happen. Would get a Security exception instead
381                     //throw a runtime exception in case the impossible happens.
382                     throw new InternalError("Unexpected IllegalAccessException");
383                 }
384             }
385         }
386     }
387 
388     //-------------------------------------------------------------------------
389 
390     /***
391      * <p>
392      * Adds the result of super.hashCode() to this builder.
393      * </p>
394      * 
395      * @param superHashCode
396      *            the result of calling <code>super.hashCode()</code>
397      * @return this HashCodeBuilder, used to chain calls.
398      * @since 2.0
399      */
400     public HashCodeBuilder appendSuper(int superHashCode) {
401         iTotal = iTotal * iConstant + superHashCode;
402         return this;
403     }
404 
405     //-------------------------------------------------------------------------
406 
407     /***
408      * <p>
409      * Append a <code>hashCode</code> for an <code>Object</code>.
410      * Collections are ignored.
411      * </p>
412      * 
413      * @param object
414      *            the Object to add to the <code>hashCode</code>
415      * @return this
416      */
417     public HashCodeBuilder append(Object object) {
418         if (object == null) {
419             iTotal = iTotal * iConstant;
420 
421         } else {
422             if (object.getClass().isArray() == false) {
423 
424                 // --------------------------------------------------------
425 
426                 /* ignore collections */
427                 if (!(object instanceof Collection)) {
428                     try {
429 
430                         //the simple case, not an array, just the element
431                         iTotal = iTotal * iConstant + object.hashCode();
432 
433                     } catch (Exception e) {
434                         // ignore it
435                         if (log.isDebugEnabled()) {
436                             log
437                                     .debug("Ignored field in hashCode method due to exception: "
438                                             + e.getLocalizedMessage());
439                         }
440                     }
441                 }
442 
443                 // --------------------------------------------------------
444 
445             } else {
446                 //'Switch' on type of array, to dispatch to the correct handler
447                 // This handles multi dimensional arrays
448                 if (object instanceof long[]) {
449                     append((long[]) object);
450                 } else if (object instanceof int[]) {
451                     append((int[]) object);
452                 } else if (object instanceof short[]) {
453                     append((short[]) object);
454                 } else if (object instanceof char[]) {
455                     append((char[]) object);
456                 } else if (object instanceof byte[]) {
457                     append((byte[]) object);
458                 } else if (object instanceof double[]) {
459                     append((double[]) object);
460                 } else if (object instanceof float[]) {
461                     append((float[]) object);
462                 } else if (object instanceof boolean[]) {
463                     append((boolean[]) object);
464                 } else {
465                     // Not an array of primitives
466                     append((Object[]) object);
467                 }
468             }
469         }
470         return this;
471     }
472 
473     /***
474      * <p>
475      * Append a <code>hashCode</code> for a <code>long</code>.
476      * </p>
477      * 
478      * @param value
479      *            the long to add to the <code>hashCode</code>
480      * @return this
481      */
482     public HashCodeBuilder append(long value) {
483         iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
484         return this;
485     }
486 
487     /***
488      * <p>
489      * Append a <code>hashCode</code> for an <code>int</code>.
490      * </p>
491      * 
492      * @param value
493      *            the int to add to the <code>hashCode</code>
494      * @return this
495      */
496     public HashCodeBuilder append(int value) {
497         iTotal = iTotal * iConstant + value;
498         return this;
499     }
500 
501     /***
502      * <p>
503      * Append a <code>hashCode</code> for a <code>short</code>.
504      * </p>
505      * 
506      * @param value
507      *            the short to add to the <code>hashCode</code>
508      * @return this
509      */
510     public HashCodeBuilder append(short value) {
511         iTotal = iTotal * iConstant + value;
512         return this;
513     }
514 
515     /***
516      * <p>
517      * Append a <code>hashCode</code> for a <code>char</code>.
518      * </p>
519      * 
520      * @param value
521      *            the char to add to the <code>hashCode</code>
522      * @return this
523      */
524     public HashCodeBuilder append(char value) {
525         iTotal = iTotal * iConstant + value;
526         return this;
527     }
528 
529     /***
530      * <p>
531      * Append a <code>hashCode</code> for a <code>byte</code>.
532      * </p>
533      * 
534      * @param value
535      *            the byte to add to the <code>hashCode</code>
536      * @return this
537      */
538     public HashCodeBuilder append(byte value) {
539         iTotal = iTotal * iConstant + value;
540         return this;
541     }
542 
543     /***
544      * <p>
545      * Append a <code>hashCode</code> for a <code>double</code>.
546      * </p>
547      * 
548      * @param value
549      *            the double to add to the <code>hashCode</code>
550      * @return this
551      */
552     public HashCodeBuilder append(double value) {
553         return append(Double.doubleToLongBits(value));
554     }
555 
556     /***
557      * <p>
558      * Append a <code>hashCode</code> for a <code>float</code>.
559      * </p>
560      * 
561      * @param value
562      *            the float to add to the <code>hashCode</code>
563      * @return this
564      */
565     public HashCodeBuilder append(float value) {
566         iTotal = iTotal * iConstant + Float.floatToIntBits(value);
567         return this;
568     }
569 
570     /***
571      * <p>
572      * Append a <code>hashCode</code> for a <code>boolean</code>.
573      * </p>
574      * 
575      * @param value
576      *            the boolean to add to the <code>hashCode</code>
577      * @return this
578      */
579     public HashCodeBuilder append(boolean value) {
580         iTotal = iTotal * iConstant + (value ? 0 : 1);
581         return this;
582     }
583 
584     /***
585      * <p>
586      * Append a <code>hashCode</code> for an <code>Object</code> array.
587      * </p>
588      * 
589      * @param array
590      *            the array to add to the <code>hashCode</code>
591      * @return this
592      */
593     public HashCodeBuilder append(Object[] array) {
594         if (array == null) {
595             iTotal = iTotal * iConstant;
596         } else {
597             for (int i = 0; i < array.length; i++) {
598                 append(array[i]);
599             }
600         }
601         return this;
602     }
603 
604     /***
605      * <p>
606      * Append a <code>hashCode</code> for a <code>long</code> array.
607      * </p>
608      * 
609      * @param array
610      *            the array to add to the <code>hashCode</code>
611      * @return this
612      */
613     public HashCodeBuilder append(long[] array) {
614         if (array == null) {
615             iTotal = iTotal * iConstant;
616         } else {
617             for (int i = 0; i < array.length; i++) {
618                 append(array[i]);
619             }
620         }
621         return this;
622     }
623 
624     /***
625      * <p>
626      * Append a <code>hashCode</code> for an <code>int</code> array.
627      * </p>
628      * 
629      * @param array
630      *            the array to add to the <code>hashCode</code>
631      * @return this
632      */
633     public HashCodeBuilder append(int[] array) {
634         if (array == null) {
635             iTotal = iTotal * iConstant;
636         } else {
637             for (int i = 0; i < array.length; i++) {
638                 append(array[i]);
639             }
640         }
641         return this;
642     }
643 
644     /***
645      * <p>
646      * Append a <code>hashCode</code> for a <code>short</code> array.
647      * </p>
648      * 
649      * @param array
650      *            the array to add to the <code>hashCode</code>
651      * @return this
652      */
653     public HashCodeBuilder append(short[] array) {
654         if (array == null) {
655             iTotal = iTotal * iConstant;
656         } else {
657             for (int i = 0; i < array.length; i++) {
658                 append(array[i]);
659             }
660         }
661         return this;
662     }
663 
664     /***
665      * <p>
666      * Append a <code>hashCode</code> for a <code>char</code> array.
667      * </p>
668      * 
669      * @param array
670      *            the array to add to the <code>hashCode</code>
671      * @return this
672      */
673     public HashCodeBuilder append(char[] array) {
674         if (array == null) {
675             iTotal = iTotal * iConstant;
676         } else {
677             for (int i = 0; i < array.length; i++) {
678                 append(array[i]);
679             }
680         }
681         return this;
682     }
683 
684     /***
685      * <p>
686      * Append a <code>hashCode</code> for a <code>byte</code> array.
687      * </p>
688      * 
689      * @param array
690      *            the array to add to the <code>hashCode</code>
691      * @return this
692      */
693     public HashCodeBuilder append(byte[] array) {
694         if (array == null) {
695             iTotal = iTotal * iConstant;
696         } else {
697             for (int i = 0; i < array.length; i++) {
698                 append(array[i]);
699             }
700         }
701         return this;
702     }
703 
704     /***
705      * <p>
706      * Append a <code>hashCode</code> for a <code>double</code> array.
707      * </p>
708      * 
709      * @param array
710      *            the array to add to the <code>hashCode</code>
711      * @return this
712      */
713     public HashCodeBuilder append(double[] array) {
714         if (array == null) {
715             iTotal = iTotal * iConstant;
716         } else {
717             for (int i = 0; i < array.length; i++) {
718                 append(array[i]);
719             }
720         }
721         return this;
722     }
723 
724     /***
725      * <p>
726      * Append a <code>hashCode</code> for a <code>float</code> array.
727      * </p>
728      * 
729      * @param array
730      *            the array to add to the <code>hashCode</code>
731      * @return this
732      */
733     public HashCodeBuilder append(float[] array) {
734         if (array == null) {
735             iTotal = iTotal * iConstant;
736         } else {
737             for (int i = 0; i < array.length; i++) {
738                 append(array[i]);
739             }
740         }
741         return this;
742     }
743 
744     /***
745      * <p>
746      * Append a <code>hashCode</code> for a <code>boolean</code> array.
747      * </p>
748      * 
749      * @param array
750      *            the array to add to the <code>hashCode</code>
751      * @return this
752      */
753     public HashCodeBuilder append(boolean[] array) {
754         if (array == null) {
755             iTotal = iTotal * iConstant;
756         } else {
757             for (int i = 0; i < array.length; i++) {
758                 append(array[i]);
759             }
760         }
761         return this;
762     }
763 
764     /***
765      * <p>
766      * Return the computed <code>hashCode</code>.
767      * </p>
768      * 
769      * @return <code>hashCode</code> based on the fields appended
770      */
771     public int toHashCode() {
772         return iTotal;
773     }
774 
775 }