%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jetspeed.util.HashCodeBuilder |
|
|
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 | 0 | 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 | 0 | super(); |
90 | 0 | iConstant = 37; |
91 | 0 | iTotal = 17; |
92 | 0 | } |
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, class="keyword">int multiplierNonZeroOddNumber) |
|
105 | { |
|
106 | 0 | super(); |
107 | 0 | if (initialNonZeroOddNumber == 0) |
108 | { |
|
109 | 0 | throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); |
110 | } |
|
111 | 0 | if (initialNonZeroOddNumber % 2 == 0) |
112 | { |
|
113 | 0 | throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); |
114 | } |
|
115 | 0 | if (multiplierNonZeroOddNumber == 0) |
116 | { |
|
117 | 0 | throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); |
118 | } |
|
119 | 0 | if (multiplierNonZeroOddNumber % 2 == 0) |
120 | { |
|
121 | 0 | throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); |
122 | } |
|
123 | 0 | iConstant = multiplierNonZeroOddNumber; |
124 | 0 | iTotal = initialNonZeroOddNumber; |
125 | 0 | } |
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 | 0 | 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 | 0 | 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(class="keyword">int initialNonZeroOddNumber, class="keyword">int multiplierNonZeroOddNumber, Object object) |
|
198 | { |
|
199 | 0 | 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 | 0 | if (object == null) |
234 | { |
|
235 | 0 | throw new IllegalArgumentException("The object to build a hash code for must not be null"); |
236 | } |
|
237 | 0 | HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber); |
238 | 0 | Field[] fields = object.getClass().getDeclaredFields(); |
239 | 0 | Field.setAccessible(fields, true); |
240 | 0 | for (int i = 0; i < fields.length; ++i) |
241 | { |
|
242 | 0 | Field f = fields[i]; |
243 | 0 | if (testTransients || !Modclass="keyword">ifier.isTransient(f.getModclass="keyword">ifiers())) |
244 | { |
|
245 | 0 | if (!Modclass="keyword">ifier.isStatic(f.getModclass="keyword">ifiers())) |
246 | { |
|
247 | try |
|
248 | { |
|
249 | 0 | hashCodeBuilder.append(f.get(object)); |
250 | } |
|
251 | 0 | 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 | 0 | throw new InternalError("Unexpected IllegalAccessException"); |
256 | 0 | } |
257 | } |
|
258 | } |
|
259 | } |
|
260 | 0 | 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 | 0 | if (object == null) |
274 | { |
|
275 | 0 | iTotal = iTotal * iConstant; |
276 | ||
277 | } |
|
278 | else |
|
279 | { |
|
280 | 0 | if (object.getClass().isArray() == false) |
281 | { |
|
282 | //the simple case, not an array, just the element |
|
283 | 0 | 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 | 0 | if (object instanceof long[]) |
291 | { |
|
292 | 0 | append((long[]) object); |
293 | } |
|
294 | 0 | else if (object instanceof int[]) |
295 | { |
|
296 | 0 | append((int[]) object); |
297 | } |
|
298 | 0 | else if (object instanceof short[]) |
299 | { |
|
300 | 0 | append((short[]) object); |
301 | } |
|
302 | 0 | else if (object instanceof char[]) |
303 | { |
|
304 | 0 | append((char[]) object); |
305 | } |
|
306 | 0 | else if (object instanceof byte[]) |
307 | { |
|
308 | 0 | append((byte[]) object); |
309 | } |
|
310 | 0 | else if (object instanceof double[]) |
311 | { |
|
312 | 0 | append((double[]) object); |
313 | } |
|
314 | 0 | else if (object instanceof float[]) |
315 | { |
|
316 | 0 | append((float[]) object); |
317 | } |
|
318 | 0 | else if (object instanceof boolean[]) |
319 | { |
|
320 | 0 | append((boolean[]) object); |
321 | } |
|
322 | else |
|
323 | { |
|
324 | // Not an array of primitives |
|
325 | 0 | append((Object[]) object); |
326 | } |
|
327 | } |
|
328 | } |
|
329 | 0 | 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 | 0 | iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32))); |
341 | 0 | 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 | 0 | iTotal = iTotal * iConstant + value; |
353 | 0 | 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 | 0 | iTotal = iTotal * iConstant + value; |
365 | 0 | 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 | 0 | iTotal = iTotal * iConstant + value; |
377 | 0 | 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 | 0 | iTotal = iTotal * iConstant + value; |
389 | 0 | 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 | 0 | 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 | 0 | iTotal = iTotal * iConstant + Float.floatToIntBits(value); |
412 | 0 | 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 | 0 | iTotal = iTotal * iConstant + (value ? 0 : 1); |
424 | 0 | 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 | 0 | if (array == null) |
436 | { |
|
437 | 0 | iTotal = iTotal * iConstant; |
438 | } |
|
439 | else |
|
440 | { |
|
441 | 0 | for (int i = 0; i < array.length; i++) |
442 | { |
|
443 | 0 | append(array[i]); |
444 | } |
|
445 | } |
|
446 | 0 | 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 | 0 | if (array == null) |
458 | { |
|
459 | 0 | iTotal = iTotal * iConstant; |
460 | } |
|
461 | else |
|
462 | { |
|
463 | 0 | for (int i = 0; i < array.length; i++) |
464 | { |
|
465 | 0 | append(array[i]); |
466 | } |
|
467 | } |
|
468 | 0 | 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 | 0 | if (array == null) |
480 | { |
|
481 | 0 | iTotal = iTotal * iConstant; |
482 | } |
|
483 | else |
|
484 | { |
|
485 | 0 | for (int i = 0; i < array.length; i++) |
486 | { |
|
487 | 0 | append(array[i]); |
488 | } |
|
489 | } |
|
490 | 0 | 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 | 0 | if (array == null) |
502 | { |
|
503 | 0 | iTotal = iTotal * iConstant; |
504 | } |
|
505 | else |
|
506 | { |
|
507 | 0 | for (int i = 0; i < array.length; i++) |
508 | { |
|
509 | 0 | append(array[i]); |
510 | } |
|
511 | } |
|
512 | 0 | 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 | 0 | if (array == null) |
524 | { |
|
525 | 0 | iTotal = iTotal * iConstant; |
526 | } |
|
527 | else |
|
528 | { |
|
529 | 0 | for (int i = 0; i < array.length; i++) |
530 | { |
|
531 | 0 | append(array[i]); |
532 | } |
|
533 | } |
|
534 | 0 | 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 | 0 | if (array == null) |
546 | { |
|
547 | 0 | iTotal = iTotal * iConstant; |
548 | } |
|
549 | else |
|
550 | { |
|
551 | 0 | for (int i = 0; i < array.length; i++) |
552 | { |
|
553 | 0 | append(array[i]); |
554 | } |
|
555 | } |
|
556 | 0 | 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 | 0 | if (array == null) |
568 | { |
|
569 | 0 | iTotal = iTotal * iConstant; |
570 | } |
|
571 | else |
|
572 | { |
|
573 | 0 | for (int i = 0; i < array.length; i++) |
574 | { |
|
575 | 0 | append(array[i]); |
576 | } |
|
577 | } |
|
578 | 0 | 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 | 0 | if (array == null) |
590 | { |
|
591 | 0 | iTotal = iTotal * iConstant; |
592 | } |
|
593 | else |
|
594 | { |
|
595 | 0 | for (int i = 0; i < array.length; i++) |
596 | { |
|
597 | 0 | append(array[i]); |
598 | } |
|
599 | } |
|
600 | 0 | 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 | 0 | if (array == null) |
612 | { |
|
613 | 0 | iTotal = iTotal * iConstant; |
614 | } |
|
615 | else |
|
616 | { |
|
617 | 0 | for (int i = 0; i < array.length; i++) |
618 | { |
|
619 | 0 | append(array[i]); |
620 | } |
|
621 | } |
|
622 | 0 | 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 | 0 | return iTotal; |
633 | } |
|
634 | ||
635 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |