View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.util;
18  
19  import java.lang.reflect.Field;
20  import java.lang.reflect.Modifier;
21  /***
22   * <code>HashCode</code> generation routines.
23   * <p>
24   * This class enables a good hashcode to be built for any class. It follows
25   * the rules laid out in the book Effective Java, by Joshua Bloch. Writing a 
26   * good hashCode is actually quite difficult. This class aims to simplify the 
27   * process.
28   * <p>
29   * All relevant fields from the object should be included in the hashCode. Derived
30   * fields may be excluded. In general, any field used in the equals method must be
31   * used in the hashCode method. 
32   * <p>
33   * To use this class write code as follows:
34   * <pre>
35   * public class Person {
36   *   String name;
37   *   int age;
38   *   boolean isSmoker;
39   *   ...
40   * 
41   *   public int hashCode() {
42   *     // you pick a hard-coded, randomly chosen, non-zero, odd number
43   *     // ideally different for each class
44   *     return new HashCodeBuilder(17, 37).   
45   *       append(name).
46   *       append(age).
47   *       append(smoker).
48   *       toHashCode();
49   *   }
50   * }
51   * </pre>
52   * <p>
53   * Alternatively, there is a method that uses reflection to determine
54   * the fields to test. Because these fields are usually private, the method, 
55   * <code>reflectionHashCode</code>, uses <code>Field.setAccessible</code> to
56   * change the visibility of the fields. This will fail under a security manager, 
57   * unless the appropriate permissions are set. It is also slower than testing 
58   * explicitly.
59   * <p>
60   * A typical invocation for this method would look like:
61   * <pre>
62   * public boolean hashCode(Object o) {
63   *   return HashCodeBuilder.reflectionHashCode(this);
64   * }
65   * </pre>
66   * 
67   * @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
68   * @version $Id: HashCodeBuilder.java 517121 2007-03-12 07:45:49Z ate $
69   */
70  public class HashCodeBuilder
71  {
72  
73      /***
74       * Constant to use in building the hashCode
75       */
76      private final int iConstant;
77      /***
78       * Running total of the hashCode
79       */
80      private int iTotal = 0;
81  
82      /***
83       * Constructor for HashCodeBuilder.
84       * This constructor uses two hard coded choices for the constants needed
85       * to build a hashCode.
86       */
87      public HashCodeBuilder()
88      {
89          super();
90          iConstant = 37;
91          iTotal = 17;
92      }
93  
94      /***
95       * Constructor for HashCodeBuilder.
96       * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
97       * these should be different for each class, however this is not vital.
98       * Prime numbers are preferred, especially for the multiplier.
99       * 
100      * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
101      * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
102      * @throws IllegalArgumentException if the number is zero or even
103      */
104     public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber)
105     {
106         super();
107         if (initialNonZeroOddNumber == 0)
108         {
109             throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
110         }
111         if (initialNonZeroOddNumber % 2 == 0)
112         {
113             throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
114         }
115         if (multiplierNonZeroOddNumber == 0)
116         {
117             throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
118         }
119         if (multiplierNonZeroOddNumber % 2 == 0)
120         {
121             throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
122         }
123         iConstant = multiplierNonZeroOddNumber;
124         iTotal = initialNonZeroOddNumber;
125     }
126 
127     //-------------------------------------------------------------------------
128 
129     /***
130      * This method uses reflection to build a valid hash code. 
131      * <p>
132      * It uses Field.setAccessible to gain access to private fields. This means
133      * that it will throw a security exception if run under a security manger, if
134      * the permissions are not set up.
135      * It is also not as efficient as testing explicitly. 
136      * Transient members will be not be used, as they are likely derived 
137      * fields, and not part of the value of the object. 
138      * Static fields will not be tested.
139      * This constructor uses two hard coded choices for the constants needed
140      * to build a hash code.
141      * 
142      * @param object  the object to create a hash code for
143      * @return int hash code
144      * @throws IllegalArgumentException if the object is null
145      */
146     public static int reflectionHashCode(Object object)
147     {
148         return reflectionHashCode(object, false);
149     }
150 
151     /***
152      * This method uses reflection to build a valid hash code. 
153      * <p>
154      * It uses Field.setAccessible to gain access to private fields. This means
155      * that it will throw a security exception if run under a security manger, if
156      * the permissions are not set up.
157      * It is also not as efficient as testing explicitly. 
158      * If the TestTransients parameter is set to true, transient members will be
159      * tested, otherwise they are ignored, as they are likely derived fields, and
160      * not part of the value of the object. 
161      * Static fields will not be tested.
162      * This constructor uses two hard coded choices for the constants needed
163      * to build a hash code.
164      * 
165      * @param object  the object to create a hash code for
166      * @param testTransients  whether to include transient fields
167      * @return int hash code
168      * @throws IllegalArgumentException if the object is null
169      */
170     public static int reflectionHashCode(Object object, boolean testTransients)
171     {
172         return reflectionHashCode(17, 37, object, testTransients);
173     }
174 
175     /***
176      * This method uses reflection to build a valid hash code. 
177      * <p>
178      * It uses Field.setAccessible to gain access to private fields. This means
179      * that it will throw a security exception if run under a security manger, if
180      * the permissions are not set up.
181      * It is also not as efficient as testing explicitly. 
182      * Transient members will be not be used, as they are likely derived 
183      * fields, and not part of the value of the object. 
184      * Static fields will not be tested.
185      * <p>
186      * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
187      * these should be different for each class, however this is not vital.
188      * Prime numbers are preferred, especially for the multiplier.
189      * 
190      * @param initialNonZeroOddNumber  a non-zero, odd number used as the initial value
191      * @param multiplierNonZeroOddNumber  a non-zero, odd number used as the multiplier
192      * @param object  the object to create a hash code for
193      * @return int hash code
194      * @throws IllegalArgumentException if the object is null
195      * @throws IllegalArgumentException if the number is zero or even
196      */
197     public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object)
198     {
199         return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false);
200     }
201 
202     /***
203      * This method uses reflection to build a valid hash code. 
204      * <p>
205      * It uses Field.setAccessible to gain access to private fields. This means
206      * that it will throw a security exception if run under a security manger, if
207      * the permissions are not set up.
208      * It is also not as efficient as testing explicitly. 
209      * If the TestTransients parameter is set to true, transient members will be
210      * tested, otherwise they are ignored, as they are likely derived fields, and
211      * not part of the value of the object. 
212      * Static fields will not be tested.
213      * <p>
214      * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
215      * these should be different for each class, however this is not vital.
216      * Prime numbers are preferred, especially for the multiplier.
217      * 
218      * @param initialNonZeroOddNumber
219      * @param multiplierNonZeroOddNumber
220      * @param object  the object to create a hash code for
221      * @param testTransients  whether to include transient fields
222      * @return int hash code
223      * @throws IllegalArgumentException if the object is null
224      * @throws IllegalArgumentException if the number is zero or even
225      */
226     public static int reflectionHashCode(
227         int initialNonZeroOddNumber,
228         int multiplierNonZeroOddNumber,
229         Object object,
230         boolean testTransients)
231     {
232 
233         if (object == null)
234         {
235             throw new IllegalArgumentException("The object to build a hash code for must not be null");
236         }
237         HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
238         Field[] fields = object.getClass().getDeclaredFields();
239         Field.setAccessible(fields, true);
240         for (int i = 0; i < fields.length; ++i)
241         {
242             Field f = fields[i];
243             if (testTransients || !Modifier.isTransient(f.getModifiers()))
244             {
245                 if (!Modifier.isStatic(f.getModifiers()))
246                 {
247                     try
248                     {
249                         hashCodeBuilder.append(f.get(object));
250                     }
251                     catch (IllegalAccessException e)
252                     {
253                         //this can't happen. Would get a Security exception instead
254                         //throw a runtime exception in case the impossible happens.
255                         throw new InternalError("Unexpected IllegalAccessException");
256                     }
257                 }
258             }
259         }
260         return hashCodeBuilder.toHashCode();
261     }
262 
263     //-------------------------------------------------------------------------
264 
265     /***
266      * Append a hashCode for an Object.
267      *
268      * @param object  the object to add to the hashCode
269      * @return this
270      */
271     public HashCodeBuilder append(Object object)
272     {
273         if (object == null)
274         {
275             iTotal = iTotal * iConstant;
276 
277         }
278         else
279         {
280             if (object.getClass().isArray() == false)
281             {
282                 //the simple case, not an array, just the element 
283                 iTotal = iTotal * iConstant + object.hashCode();
284 
285             }
286             else
287             {
288                 //'Switch' on type of array, to dispatch to the correct handler
289                 // This handles multi dimensional arrays
290                 if (object instanceof long[])
291                 {
292                     append((long[]) object);
293                 }
294                 else if (object instanceof int[])
295                 {
296                     append((int[]) object);
297                 }
298                 else if (object instanceof short[])
299                 {
300                     append((short[]) object);
301                 }
302                 else if (object instanceof char[])
303                 {
304                     append((char[]) object);
305                 }
306                 else if (object instanceof byte[])
307                 {
308                     append((byte[]) object);
309                 }
310                 else if (object instanceof double[])
311                 {
312                     append((double[]) object);
313                 }
314                 else if (object instanceof float[])
315                 {
316                     append((float[]) object);
317                 }
318                 else if (object instanceof boolean[])
319                 {
320                     append((boolean[]) object);
321                 }
322                 else
323                 {
324                     // Not an array of primitives
325                     append((Object[]) object);
326                 }
327             }
328         }
329         return this;
330     }
331 
332     /***
333      * Append a hashCode for a long.
334      *
335      * @param value  the long to add to the hashCode
336      * @return this
337      */
338     public HashCodeBuilder append(long value)
339     {
340         iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
341         return this;
342     }
343 
344     /***
345      * Append a hashCode for an int.
346      *
347      * @param value  the int to add to the hashCode
348      * @return this
349      */
350     public HashCodeBuilder append(int value)
351     {
352         iTotal = iTotal * iConstant + value;
353         return this;
354     }
355 
356     /***
357      * Append a hashCode for a short.
358      *
359      * @param value  the short to add to the hashCode
360      * @return this
361      */
362     public HashCodeBuilder append(short value)
363     {
364         iTotal = iTotal * iConstant + value;
365         return this;
366     }
367 
368     /***
369      * Append a hashCode for a char.
370      *
371      * @param value  the char to add to the hashCode
372      * @return this
373      */
374     public HashCodeBuilder append(char value)
375     {
376         iTotal = iTotal * iConstant + value;
377         return this;
378     }
379 
380     /***
381      * Append a hashCode for a byte.
382      *
383      * @param value  the byte to add to the hashCode
384      * @return this
385      */
386     public HashCodeBuilder append(byte value)
387     {
388         iTotal = iTotal * iConstant + value;
389         return this;
390     }
391 
392     /***
393      * Append a hashCode for a double.
394      *
395      * @param value  the double to add to the hashCode
396      * @return this
397      */
398     public HashCodeBuilder append(double value)
399     {
400         return append(Double.doubleToLongBits(value));
401     }
402 
403     /***
404      * Append a hashCode for a float.
405      *
406      * @param value  the float to add to the hashCode
407      * @return this
408      */
409     public HashCodeBuilder append(float value)
410     {
411         iTotal = iTotal * iConstant + Float.floatToIntBits(value);
412         return this;
413     }
414 
415     /***
416      * Append a hashCode for a long.
417      *
418      * @param value  the long to add to the hashCode
419      * @return this
420      */
421     public HashCodeBuilder append(boolean value)
422     {
423         iTotal = iTotal * iConstant + (value ? 0 : 1);
424         return this;
425     }
426 
427     /***
428      * Append a hashCode for an Object array.
429      *
430      * @param array  the array to add to the hashCode
431      * @return this
432      */
433     public HashCodeBuilder append(Object[] array)
434     {
435         if (array == null)
436         {
437             iTotal = iTotal * iConstant;
438         }
439         else
440         {
441             for (int i = 0; i < array.length; i++)
442             {
443                 append(array[i]);
444             }
445         }
446         return this;
447     }
448 
449     /***
450      * Append a hashCode for a long array.
451      *
452      * @param array  the array to add to the hashCode
453      * @return this
454      */
455     public HashCodeBuilder append(long[] array)
456     {
457         if (array == null)
458         {
459             iTotal = iTotal * iConstant;
460         }
461         else
462         {
463             for (int i = 0; i < array.length; i++)
464             {
465                 append(array[i]);
466             }
467         }
468         return this;
469     }
470 
471     /***
472      * Append a hashCode for an int array.
473      *
474      * @param array  the array to add to the hashCode
475      * @return this
476      */
477     public HashCodeBuilder append(int[] array)
478     {
479         if (array == null)
480         {
481             iTotal = iTotal * iConstant;
482         }
483         else
484         {
485             for (int i = 0; i < array.length; i++)
486             {
487                 append(array[i]);
488             }
489         }
490         return this;
491     }
492 
493     /***
494      * Append a hashCode for a short array.
495      *
496      * @param array  the array to add to the hashCode
497      * @return this
498      */
499     public HashCodeBuilder append(short[] array)
500     {
501         if (array == null)
502         {
503             iTotal = iTotal * iConstant;
504         }
505         else
506         {
507             for (int i = 0; i < array.length; i++)
508             {
509                 append(array[i]);
510             }
511         }
512         return this;
513     }
514 
515     /***
516      * Append a hashCode for a char array.
517      *
518      * @param array  the array to add to the hashCode
519      * @return this
520      */
521     public HashCodeBuilder append(char[] array)
522     {
523         if (array == null)
524         {
525             iTotal = iTotal * iConstant;
526         }
527         else
528         {
529             for (int i = 0; i < array.length; i++)
530             {
531                 append(array[i]);
532             }
533         }
534         return this;
535     }
536 
537     /***
538      * Append a hashCode for a byte array.
539      *
540      * @param array  the array to add to the hashCode
541      * @return this
542      */
543     public HashCodeBuilder append(byte[] array)
544     {
545         if (array == null)
546         {
547             iTotal = iTotal * iConstant;
548         }
549         else
550         {
551             for (int i = 0; i < array.length; i++)
552             {
553                 append(array[i]);
554             }
555         }
556         return this;
557     }
558 
559     /***
560      * Append a hashCode for a double array.
561      *
562      * @param array  the array to add to the hashCode
563      * @return this
564      */
565     public HashCodeBuilder append(double[] array)
566     {
567         if (array == null)
568         {
569             iTotal = iTotal * iConstant;
570         }
571         else
572         {
573             for (int i = 0; i < array.length; i++)
574             {
575                 append(array[i]);
576             }
577         }
578         return this;
579     }
580 
581     /***
582      * Append a hashCode for a float array.
583      *
584      * @param array  the array to add to the hashCode
585      * @return this
586      */
587     public HashCodeBuilder append(float[] array)
588     {
589         if (array == null)
590         {
591             iTotal = iTotal * iConstant;
592         }
593         else
594         {
595             for (int i = 0; i < array.length; i++)
596             {
597                 append(array[i]);
598             }
599         }
600         return this;
601     }
602 
603     /***
604      * Append a hashCode for a boolean array.
605      *
606      * @param array  the array to add to the hashCode
607      * @return this
608      */
609     public HashCodeBuilder append(boolean[] array)
610     {
611         if (array == null)
612         {
613             iTotal = iTotal * iConstant;
614         }
615         else
616         {
617             for (int i = 0; i < array.length; i++)
618             {
619                 append(array[i]);
620             }
621         }
622         return this;
623     }
624 
625     /***
626      * Return the computed hashCode
627      * 
628      * @return int hashCode based on the fields appended
629      */
630     public int toHashCode()
631     {
632         return iTotal;
633     }
634 
635 }