/*
* Copyright 2010 Daniel Bell
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.guit.client.command;
/**
* <p>
* Assists in implementing {@link Object#hashCode()} methods.
* </p>
* <p>
* This class enables a good <code>hashCode</code> method to be built for any
* class. It follows the rules laid out in the book <a
* href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
* by Joshua Bloch. Writing a good <code>hashCode</code> method is actually
* quite difficult. This class aims to simplify the process.
* </p>
* <p>
* All relevant fields from the object should be included in the
* <code>hashCode</code> method. Derived fields may be excluded. In general, any
* field used in the <code>equals</code> method must be used in the
* <code>hashCode</code> method.
* </p>
* <p>
* To use this class write code as follows:
* </p>
*
* <pre>
* public class Person {
* String name;
* int age;
* boolean smoker;
* ...
*
* public int hashCode() {
* // you pick a hard-coded, randomly chosen, non-zero, odd number
* // ideally different for each class
* return new HashCodeBuilder(17, 37).
* append(name).
* append(age).
* append(smoker).
* toHashCode();
* }
* }
* </pre>
* <p>
* If required, the superclass <code>hashCode()</code> can be added using
* {@link #appendSuper}.
* </p>
* <p>
* Alternatively, there is a method that uses reflection to determine the fields
* to test. Because these fields are usually private, the method,
* <code>reflectionHashCode</code>, uses
* <code>AccessibleObject.setAccessible</code> to change the visibility of the
* fields. This will fail under a security manager, unless the appropriate
* permissions are set up correctly. It is also slower than testing explicitly.
* </p>
* <p>
* A typical invocation for this method would look like:
* </p>
*
* <pre>
* public int hashCode() {
* return HashCodeBuilder.reflectionHashCode(this);
* }
* </pre>
*
* @since 1.0
*/
public class HashCodeBuilder {
public static final class DoubleUtil {
static long doubleToLongBits(final double v) {
if (Double.isNaN(v)) {
// NaN -> 0 11111111 10000000000000000000000
// IEEE754, NaN exponent bits all 1s, and mantissa is non-zero
return 0x0FFFL << 51;
}
// value < 0 -> 1 ???????? ???????????????????????
// value > 0 -> 0 ???????? ???????????????????????
long sign = (v < 0 ? 0x1L << 63 : 0);
long exponent = 0;
double absV = Math.abs(v);
// Infinity -> ? 11111111 00000000000000000000000
// IEEE754 infinite numbers, exponent all 1s, mantissa is 0
if (Double.isInfinite(v)) {
exponent = 0x07FFL << 52;
} else {
if (absV == 0.0) {
// 0.0 -> ? 00000000 00000000000000000000000
// IEEE754, exponent is 0, mantissa is zero
// we don't handle negative zero at the moment, it is treated as
// positive zero
exponent = 0L;
} else {
// get an approximation to the exponent
// let d = 1.M * 2^E
// log2(d) = log2(1.M * 2^E)
// log2(d) = log2(1.M) + log2(2^E)
// log2(d) = log2(1.M) + E
// floor(log2(d)) = floor(log(1.M) + E)
// floor(log2(d)) = E (because log(1.M) always < 1)
// E = floor(log2(d))
// E = floor(log(d)/log(2))
int guess = (int) Math.floor(Math.log(absV) / Math.log(2));
// force it to -1023, 1023 interval (<= -1023 = denorm/zero)
guess = Math.max(-1023, Math.min(guess, 1023));
// Recall that d = 1.M * 2^E, so dividing by 2^E should leave
// us with 1.M
// divide away exponent guess
double exp = Math.pow(2, guess);
absV = absV / exp;
// while the number is still bigger than a normalized number
// increment exponent guess
// This might occur if there is some precision loss in determining
// the exponent
while (absV > 2.0) {
guess++;
absV /= 2.0;
}
// if the number is smaller than a normalized number
// decrement exponent. If the exponent becomes zero, and we
// fail to achieve a normalized mantissa, then this number
// must be a denormalized value
while (absV < 1 && guess > 1024) {
guess--;
absV *= 2;
}
exponent = (guess + 1023L) << 52;
}
}
// if denormalized
if (exponent <= 0) {
// denormalized numbers have an exponent of zero, but pretend
// they have an exponent of 1, so since there is an implicit
// * 2^1 for denorms, we correct by dividing by 2
absV /= 2;
}
// the input value has now been stripped of its exponent
// and is in the range [0,2), we strip off the leading decimal
// and use the remainer as a percentage of the significand value (2^52)
long mantissa = (long) ((absV % 1) * Math.pow(2, 52));
return sign | exponent | (mantissa & 0xfffffffffffffL);
}
private DoubleUtil() {
}
}
public static class FloatUtil {
private static final double LN2 = Math.log(2);
// Theory of operation: Let a double number d be represented as
// 1.M * 2^E, where the leading bit is assumed to be 1,
// the fractional mantissa M is multiplied 2 to the power of E.
// We want to reliably recover M and E, and then encode them according
// to IEEE754 (see http://en.wikipedia.org/wiki/IEEE754)
static int floatToIntBits(float value) {
if (Float.isNaN(value)) {
return 0x7fc00000;
}
int signBit;
if (value == 0) {
return (1 / value == Float.NEGATIVE_INFINITY) ? 0x80000000 : 0;
} else if (value < 0) {
value = -value;
signBit = 0x80000000;
} else {
signBit = 0;
}
if (value == Float.POSITIVE_INFINITY) {
return signBit | 0x7f800000;
}
int exponent = (int) (Math.log(value) / LN2);
if (exponent < -126) {
exponent = -126;
}
int significand = (int) (0.5 + value * Math.exp(-(exponent - 23) * LN2));
// Handle exponent rounding issues & denorm
if ((significand & 0x01000000) != 0) {
significand >>= 1;
exponent++;
} else if ((significand & 0x00800000) == 0) {
if (exponent == -126) {
return signBit | significand;
} else {
significand <<= 1;
exponent--;
}
}
return signBit | ((exponent + 127) << 23) | (significand & 0x007fffff);
}
static String toBinaryIeee754String(long decimal) {
String binary = Long.toBinaryString(decimal);
StringBuilder result = new StringBuilder(binary);
for (long i = binary.length(); i < 32; i++) {
result.insert(0, "0");
}
result.insert(9, " ");
result.insert(1, " ");
return result.toString();
}
private FloatUtil() {
}
}
/**
* Constant to use in building the hashCode.
*/
private final int iConstant;
/**
* Running total of the hashCode.
*/
private int iTotal = 0;
/**
* <p>
* Uses two hard coded choices for the constants needed to build a
* <code>hashCode</code>.
* </p>
*/
public HashCodeBuilder() {
iConstant = 37;
iTotal = 17;
}
/**
* <p>
* Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these
* should be different for each class, however this is not vital.
* </p>
* <p>
* Prime numbers are preferred, especially for the multiplier.
* </p>
*
* @param initialNonZeroOddNumber a non-zero, odd number used as the initial
* value
* @param multiplierNonZeroOddNumber a non-zero, odd number used as the
* multiplier
* @throws IllegalArgumentException if the number is zero or even
*/
public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
if (initialNonZeroOddNumber == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
}
if (initialNonZeroOddNumber % 2 == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
}
if (multiplierNonZeroOddNumber == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
}
if (multiplierNonZeroOddNumber % 2 == 0) {
throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
}
iConstant = multiplierNonZeroOddNumber;
iTotal = initialNonZeroOddNumber;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>boolean</code>.
* </p>
* <p>
* This adds <code>iConstant * 1</code> to the <code>hashCode</code> and not a
* <code>1231</code> or <code>1237</code> as done in java.lang.Boolean. This
* is in accordance with the <quote>Effective Java</quote> design.
* </p>
*
* @param value the boolean to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(boolean value) {
iTotal = iTotal * iConstant + (value ? 0 : 1);
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>boolean</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(boolean[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
// -------------------------------------------------------------------------
/**
* <p>
* Append a <code>hashCode</code> for a <code>byte</code>.
* </p>
*
* @param value the byte to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(byte value) {
iTotal = iTotal * iConstant + value;
return this;
}
// -------------------------------------------------------------------------
/**
* <p>
* Append a <code>hashCode</code> for a <code>byte</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(byte[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>char</code>.
* </p>
*
* @param value the char to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(char value) {
iTotal = iTotal * iConstant + value;
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>char</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(char[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>double</code>.
* </p>
*
* @param value the double to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(double value) {
return append(DoubleUtil.doubleToLongBits(value));
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>double</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(double[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>float</code>.
* </p>
*
* @param value the float to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(float value) {
iTotal = iTotal * iConstant + FloatUtil.floatToIntBits(value);
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>float</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(float[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for an <code>int</code>.
* </p>
*
* @param value the int to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(int value) {
iTotal = iTotal * iConstant + value;
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for an <code>int</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(int[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>long</code>.
* </p>
*
* @param value the long to add to the <code>hashCode</code>
* @return this
*/
// NOTE: This method uses >> and not >>> as Effective Java and
// Long.hashCode do. Ideally we should switch to >>> at
// some stage. There are backwards compat issues, so
// that will have to wait for the time being. cf LANG-342.
public HashCodeBuilder append(long value) {
iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>long</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(long[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for an <code>Object</code>.
* </p>
*
* @param object the Object to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(Object object) {
if (object == null) {
iTotal = iTotal * iConstant;
} else {
if (object.getClass().isArray()) {
// 'Switch' on type of array, to dispatch to the correct handler
// This handles multi dimensional arrays
if (object instanceof long[]) {
append((long[]) object);
} else if (object instanceof int[]) {
append((int[]) object);
} else if (object instanceof short[]) {
append((short[]) object);
} else if (object instanceof char[]) {
append((char[]) object);
} else if (object instanceof byte[]) {
append((byte[]) object);
} else if (object instanceof double[]) {
append((double[]) object);
} else if (object instanceof float[]) {
append((float[]) object);
} else if (object instanceof boolean[]) {
append((boolean[]) object);
} else {
// Not an array of primitives
append((Object[]) object);
}
} else {
iTotal = iTotal * iConstant + object.hashCode();
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for an <code>Object</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(Object[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>short</code>.
* </p>
*
* @param value the short to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(short value) {
iTotal = iTotal * iConstant + value;
return this;
}
/**
* <p>
* Append a <code>hashCode</code> for a <code>short</code> array.
* </p>
*
* @param array the array to add to the <code>hashCode</code>
* @return this
*/
public HashCodeBuilder append(short[] array) {
if (array == null) {
iTotal = iTotal * iConstant;
} else {
for (int i = 0; i < array.length; i++) {
append(array[i]);
}
}
return this;
}
/**
* <p>
* Adds the result of super.hashCode() to this builder.
* </p>
*
* @param superHashCode the result of calling <code>super.hashCode()</code>
* @return this HashCodeBuilder, used to chain calls.
* @since 2.0
*/
public HashCodeBuilder appendSuper(int superHashCode) {
iTotal = iTotal * iConstant + superHashCode;
return this;
}
/**
* <p>
* Return the computed <code>hashCode</code>.
* </p>
*
* @return <code>hashCode</code> based on the fields appended
*/
public int toHashCode() {
return iTotal;
}
/**
* Returns the computed <code>hashCode</code>.
*
* @return <code>hashCode</code> based on the fields appended
* @since 3.0
*/
public Integer build() {
return toHashCode();
}
/**
* <p>
* The computed <code>hashCode</code> from toHashCode() is returned due to the
* likelyhood of bugs in mis-calling toHashCode() and the unlikelyness of it
* mattering what the hashCode for HashCodeBuilder itself is.
*
* @return <code>hashCode</code> based on the fields appended
* @since 2.5
*/
@Override
public int hashCode() {
return toHashCode();
}
}