1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
254
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
283 iTotal = iTotal * iConstant + object.hashCode();
284
285 }
286 else
287 {
288
289
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
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 }