001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.util;
018:
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Modifier;
021:
022: /**
023: * <code>HashCode</code> generation routines.
024: * <p>
025: * This class enables a good hashcode to be built for any class. It follows
026: * the rules laid out in the book Effective Java, by Joshua Bloch. Writing a
027: * good hashCode is actually quite difficult. This class aims to simplify the
028: * process.
029: * <p>
030: * All relevant fields from the object should be included in the hashCode. Derived
031: * fields may be excluded. In general, any field used in the equals method must be
032: * used in the hashCode method.
033: * <p>
034: * To use this class write code as follows:
035: * <pre>
036: * public class Person {
037: * String name;
038: * int age;
039: * boolean isSmoker;
040: * ...
041: *
042: * public int hashCode() {
043: * // you pick a hard-coded, randomly chosen, non-zero, odd number
044: * // ideally different for each class
045: * return new HashCodeBuilder(17, 37).
046: * append(name).
047: * append(age).
048: * append(smoker).
049: * toHashCode();
050: * }
051: * }
052: * </pre>
053: * <p>
054: * Alternatively, there is a method that uses reflection to determine
055: * the fields to test. Because these fields are usually private, the method,
056: * <code>reflectionHashCode</code>, uses <code>Field.setAccessible</code> to
057: * change the visibility of the fields. This will fail under a security manager,
058: * unless the appropriate permissions are set. It is also slower than testing
059: * explicitly.
060: * <p>
061: * A typical invocation for this method would look like:
062: * <pre>
063: * public boolean hashCode(Object o) {
064: * return HashCodeBuilder.reflectionHashCode(this);
065: * }
066: * </pre>
067: *
068: * @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
069: * @version $Id: HashCodeBuilder.java 517121 2007-03-12 07:45:49Z ate $
070: */
071: public class HashCodeBuilder {
072:
073: /**
074: * Constant to use in building the hashCode
075: */
076: private final int iConstant;
077: /**
078: * Running total of the hashCode
079: */
080: private int iTotal = 0;
081:
082: /**
083: * Constructor for HashCodeBuilder.
084: * This constructor uses two hard coded choices for the constants needed
085: * to build a hashCode.
086: */
087: public HashCodeBuilder() {
088: super ();
089: iConstant = 37;
090: iTotal = 17;
091: }
092:
093: /**
094: * Constructor for HashCodeBuilder.
095: * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
096: * these should be different for each class, however this is not vital.
097: * Prime numbers are preferred, especially for the multiplier.
098: *
099: * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value
100: * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier
101: * @throws IllegalArgumentException if the number is zero or even
102: */
103: public HashCodeBuilder(int initialNonZeroOddNumber,
104: int multiplierNonZeroOddNumber) {
105: super ();
106: if (initialNonZeroOddNumber == 0) {
107: throw new IllegalArgumentException(
108: "HashCodeBuilder requires a non zero initial value");
109: }
110: if (initialNonZeroOddNumber % 2 == 0) {
111: throw new IllegalArgumentException(
112: "HashCodeBuilder requires an odd initial value");
113: }
114: if (multiplierNonZeroOddNumber == 0) {
115: throw new IllegalArgumentException(
116: "HashCodeBuilder requires a non zero multiplier");
117: }
118: if (multiplierNonZeroOddNumber % 2 == 0) {
119: throw new IllegalArgumentException(
120: "HashCodeBuilder requires an odd multiplier");
121: }
122: iConstant = multiplierNonZeroOddNumber;
123: iTotal = initialNonZeroOddNumber;
124: }
125:
126: //-------------------------------------------------------------------------
127:
128: /**
129: * This method uses reflection to build a valid hash code.
130: * <p>
131: * It uses Field.setAccessible to gain access to private fields. This means
132: * that it will throw a security exception if run under a security manger, if
133: * the permissions are not set up.
134: * It is also not as efficient as testing explicitly.
135: * Transient members will be not be used, as they are likely derived
136: * fields, and not part of the value of the object.
137: * Static fields will not be tested.
138: * This constructor uses two hard coded choices for the constants needed
139: * to build a hash code.
140: *
141: * @param object the object to create a hash code for
142: * @return int hash code
143: * @throws IllegalArgumentException if the object is null
144: */
145: public static int reflectionHashCode(Object object) {
146: return reflectionHashCode(object, false);
147: }
148:
149: /**
150: * This method uses reflection to build a valid hash code.
151: * <p>
152: * It uses Field.setAccessible to gain access to private fields. This means
153: * that it will throw a security exception if run under a security manger, if
154: * the permissions are not set up.
155: * It is also not as efficient as testing explicitly.
156: * If the TestTransients parameter is set to true, transient members will be
157: * tested, otherwise they are ignored, as they are likely derived fields, and
158: * not part of the value of the object.
159: * Static fields will not be tested.
160: * This constructor uses two hard coded choices for the constants needed
161: * to build a hash code.
162: *
163: * @param object the object to create a hash code for
164: * @param testTransients whether to include transient fields
165: * @return int hash code
166: * @throws IllegalArgumentException if the object is null
167: */
168: public static int reflectionHashCode(Object object,
169: boolean testTransients) {
170: return reflectionHashCode(17, 37, object, testTransients);
171: }
172:
173: /**
174: * This method uses reflection to build a valid hash code.
175: * <p>
176: * It uses Field.setAccessible to gain access to private fields. This means
177: * that it will throw a security exception if run under a security manger, if
178: * the permissions are not set up.
179: * It is also not as efficient as testing explicitly.
180: * Transient members will be not be used, as they are likely derived
181: * fields, and not part of the value of the object.
182: * Static fields will not be tested.
183: * <p>
184: * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
185: * these should be different for each class, however this is not vital.
186: * Prime numbers are preferred, especially for the multiplier.
187: *
188: * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value
189: * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier
190: * @param object the object to create a hash code for
191: * @return int hash code
192: * @throws IllegalArgumentException if the object is null
193: * @throws IllegalArgumentException if the number is zero or even
194: */
195: public static int reflectionHashCode(int initialNonZeroOddNumber,
196: int multiplierNonZeroOddNumber, Object object) {
197: return reflectionHashCode(initialNonZeroOddNumber,
198: multiplierNonZeroOddNumber, object, false);
199: }
200:
201: /**
202: * This method uses reflection to build a valid hash code.
203: * <p>
204: * It uses Field.setAccessible to gain access to private fields. This means
205: * that it will throw a security exception if run under a security manger, if
206: * the permissions are not set up.
207: * It is also not as efficient as testing explicitly.
208: * If the TestTransients parameter is set to true, transient members will be
209: * tested, otherwise they are ignored, as they are likely derived fields, and
210: * not part of the value of the object.
211: * Static fields will not be tested.
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: *
217: * @param initialNonZeroOddNumber
218: * @param multiplierNonZeroOddNumber
219: * @param object the object to create a hash code for
220: * @param testTransients whether to include transient fields
221: * @return int hash code
222: * @throws IllegalArgumentException if the object is null
223: * @throws IllegalArgumentException if the number is zero or even
224: */
225: public static int reflectionHashCode(int initialNonZeroOddNumber,
226: int multiplierNonZeroOddNumber, Object object,
227: boolean testTransients) {
228:
229: if (object == null) {
230: throw new IllegalArgumentException(
231: "The object to build a hash code for must not be null");
232: }
233: HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(
234: initialNonZeroOddNumber, multiplierNonZeroOddNumber);
235: Field[] fields = object.getClass().getDeclaredFields();
236: Field.setAccessible(fields, true);
237: for (int i = 0; i < fields.length; ++i) {
238: Field f = fields[i];
239: if (testTransients
240: || !Modifier.isTransient(f.getModifiers())) {
241: if (!Modifier.isStatic(f.getModifiers())) {
242: try {
243: hashCodeBuilder.append(f.get(object));
244: } catch (IllegalAccessException e) {
245: //this can't happen. Would get a Security exception instead
246: //throw a runtime exception in case the impossible happens.
247: throw new InternalError(
248: "Unexpected IllegalAccessException");
249: }
250: }
251: }
252: }
253: return hashCodeBuilder.toHashCode();
254: }
255:
256: //-------------------------------------------------------------------------
257:
258: /**
259: * Append a hashCode for an Object.
260: *
261: * @param object the object to add to the hashCode
262: * @return this
263: */
264: public HashCodeBuilder append(Object object) {
265: if (object == null) {
266: iTotal = iTotal * iConstant;
267:
268: } else {
269: if (object.getClass().isArray() == false) {
270: //the simple case, not an array, just the element
271: iTotal = iTotal * iConstant + object.hashCode();
272:
273: } else {
274: //'Switch' on type of array, to dispatch to the correct handler
275: // This handles multi dimensional arrays
276: if (object instanceof long[]) {
277: append((long[]) object);
278: } else if (object instanceof int[]) {
279: append((int[]) object);
280: } else if (object instanceof short[]) {
281: append((short[]) object);
282: } else if (object instanceof char[]) {
283: append((char[]) object);
284: } else if (object instanceof byte[]) {
285: append((byte[]) object);
286: } else if (object instanceof double[]) {
287: append((double[]) object);
288: } else if (object instanceof float[]) {
289: append((float[]) object);
290: } else if (object instanceof boolean[]) {
291: append((boolean[]) object);
292: } else {
293: // Not an array of primitives
294: append((Object[]) object);
295: }
296: }
297: }
298: return this ;
299: }
300:
301: /**
302: * Append a hashCode for a long.
303: *
304: * @param value the long to add to the hashCode
305: * @return this
306: */
307: public HashCodeBuilder append(long value) {
308: iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
309: return this ;
310: }
311:
312: /**
313: * Append a hashCode for an int.
314: *
315: * @param value the int to add to the hashCode
316: * @return this
317: */
318: public HashCodeBuilder append(int value) {
319: iTotal = iTotal * iConstant + value;
320: return this ;
321: }
322:
323: /**
324: * Append a hashCode for a short.
325: *
326: * @param value the short to add to the hashCode
327: * @return this
328: */
329: public HashCodeBuilder append(short value) {
330: iTotal = iTotal * iConstant + value;
331: return this ;
332: }
333:
334: /**
335: * Append a hashCode for a char.
336: *
337: * @param value the char to add to the hashCode
338: * @return this
339: */
340: public HashCodeBuilder append(char value) {
341: iTotal = iTotal * iConstant + value;
342: return this ;
343: }
344:
345: /**
346: * Append a hashCode for a byte.
347: *
348: * @param value the byte to add to the hashCode
349: * @return this
350: */
351: public HashCodeBuilder append(byte value) {
352: iTotal = iTotal * iConstant + value;
353: return this ;
354: }
355:
356: /**
357: * Append a hashCode for a double.
358: *
359: * @param value the double to add to the hashCode
360: * @return this
361: */
362: public HashCodeBuilder append(double value) {
363: return append(Double.doubleToLongBits(value));
364: }
365:
366: /**
367: * Append a hashCode for a float.
368: *
369: * @param value the float to add to the hashCode
370: * @return this
371: */
372: public HashCodeBuilder append(float value) {
373: iTotal = iTotal * iConstant + Float.floatToIntBits(value);
374: return this ;
375: }
376:
377: /**
378: * Append a hashCode for a long.
379: *
380: * @param value the long to add to the hashCode
381: * @return this
382: */
383: public HashCodeBuilder append(boolean value) {
384: iTotal = iTotal * iConstant + (value ? 0 : 1);
385: return this ;
386: }
387:
388: /**
389: * Append a hashCode for an Object array.
390: *
391: * @param array the array to add to the hashCode
392: * @return this
393: */
394: public HashCodeBuilder append(Object[] array) {
395: if (array == null) {
396: iTotal = iTotal * iConstant;
397: } else {
398: for (int i = 0; i < array.length; i++) {
399: append(array[i]);
400: }
401: }
402: return this ;
403: }
404:
405: /**
406: * Append a hashCode for a long array.
407: *
408: * @param array the array to add to the hashCode
409: * @return this
410: */
411: public HashCodeBuilder append(long[] array) {
412: if (array == null) {
413: iTotal = iTotal * iConstant;
414: } else {
415: for (int i = 0; i < array.length; i++) {
416: append(array[i]);
417: }
418: }
419: return this ;
420: }
421:
422: /**
423: * Append a hashCode for an int array.
424: *
425: * @param array the array to add to the hashCode
426: * @return this
427: */
428: public HashCodeBuilder append(int[] array) {
429: if (array == null) {
430: iTotal = iTotal * iConstant;
431: } else {
432: for (int i = 0; i < array.length; i++) {
433: append(array[i]);
434: }
435: }
436: return this ;
437: }
438:
439: /**
440: * Append a hashCode for a short array.
441: *
442: * @param array the array to add to the hashCode
443: * @return this
444: */
445: public HashCodeBuilder append(short[] array) {
446: if (array == null) {
447: iTotal = iTotal * iConstant;
448: } else {
449: for (int i = 0; i < array.length; i++) {
450: append(array[i]);
451: }
452: }
453: return this ;
454: }
455:
456: /**
457: * Append a hashCode for a char array.
458: *
459: * @param array the array to add to the hashCode
460: * @return this
461: */
462: public HashCodeBuilder append(char[] array) {
463: if (array == null) {
464: iTotal = iTotal * iConstant;
465: } else {
466: for (int i = 0; i < array.length; i++) {
467: append(array[i]);
468: }
469: }
470: return this ;
471: }
472:
473: /**
474: * Append a hashCode for a byte array.
475: *
476: * @param array the array to add to the hashCode
477: * @return this
478: */
479: public HashCodeBuilder append(byte[] array) {
480: if (array == null) {
481: iTotal = iTotal * iConstant;
482: } else {
483: for (int i = 0; i < array.length; i++) {
484: append(array[i]);
485: }
486: }
487: return this ;
488: }
489:
490: /**
491: * Append a hashCode for a double array.
492: *
493: * @param array the array to add to the hashCode
494: * @return this
495: */
496: public HashCodeBuilder append(double[] array) {
497: if (array == null) {
498: iTotal = iTotal * iConstant;
499: } else {
500: for (int i = 0; i < array.length; i++) {
501: append(array[i]);
502: }
503: }
504: return this ;
505: }
506:
507: /**
508: * Append a hashCode for a float array.
509: *
510: * @param array the array to add to the hashCode
511: * @return this
512: */
513: public HashCodeBuilder append(float[] array) {
514: if (array == null) {
515: iTotal = iTotal * iConstant;
516: } else {
517: for (int i = 0; i < array.length; i++) {
518: append(array[i]);
519: }
520: }
521: return this ;
522: }
523:
524: /**
525: * Append a hashCode for a boolean array.
526: *
527: * @param array the array to add to the hashCode
528: * @return this
529: */
530: public HashCodeBuilder append(boolean[] array) {
531: if (array == null) {
532: iTotal = iTotal * iConstant;
533: } else {
534: for (int i = 0; i < array.length; i++) {
535: append(array[i]);
536: }
537: }
538: return this ;
539: }
540:
541: /**
542: * Return the computed hashCode
543: *
544: * @return int hashCode based on the fields appended
545: */
546: public int toHashCode() {
547: return iTotal;
548: }
549:
550: }
|