1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
381
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
427 if (!(object instanceof Collection)) {
428 try {
429
430
431 iTotal = iTotal * iConstant + object.hashCode();
432
433 } catch (Exception e) {
434
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
447
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
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 }