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