HashCodeBuilder.java

package fr.sii.ogham.core.util;

/**
 * <p>
 * Assists in implementing Object.hashCode() methods.
 * </p>
 * 
 * <p>
 * This class enables a good hashCode method to be built for any class. It
 * follows the rules laid out in the book Effective Java by Joshua Bloch.
 * Writing a good hashCode method is actually quite difficult. This class aims
 * to simplify the process.
 * </p>
 * 
 * <p>
 * The following is the approach taken. When appending a data field, the current
 * total is multiplied by the multiplier then a relevant value for that data
 * type is added. For example, if the current hashCode is 17, and the multiplier
 * is 37, then appending the integer 45 will create a hashcode of 674, namely 17
 * * 37 + 45.
 * </p>
 * 
 * <p>
 * All relevant fields from the object should be included in the hashCode
 * method. Derived fields may be excluded. In general, any field used in the
 * equals method must be used in the hashCode 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).
 *        hashCode();
 *    }
 *  }
 * </pre>
 * <p>
 * If required, the superclass hashCode() can be added using 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,
 * reflectionHashCode, uses AccessibleObject.setAccessible 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>
 * 
 * @author Aurélien Baudet
 *
 */
public class HashCodeBuilder {
	private org.apache.commons.lang3.builder.HashCodeBuilder delegate;

	/**
	 * <p>
	 * Two randomly chosen, 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 initialOddNumber
	 *            an odd number used as the initial value
	 * @param multiplierOddNumber
	 *            an odd number used as the multiplier
	 * @throws IllegalArgumentException
	 *             if the number is even
	 */
	public HashCodeBuilder(int initialOddNumber, int multiplierOddNumber) {
		super();
		delegate = new org.apache.commons.lang3.builder.HashCodeBuilder(initialOddNumber, multiplierOddNumber);
	}

	/**
	 * Uses two hard coded choices for the constants needed to build a hashCode.
	 */
	public HashCodeBuilder() {
		super();
		delegate = new org.apache.commons.lang3.builder.HashCodeBuilder();
	}

	/**
	 * Append a hashCode for an Object.
	 * 
	 * @param objectValue
	 *            the Object to add to the hashCode
	 * @return used to chain calls
	 */
	public HashCodeBuilder append(Object objectValue) {
		delegate.append(objectValue);
		return this;
	}

	/**
	 * Append a hashCode for an Object.
	 * 
	 * @param objectValues
	 *            array of Object to add to the hashCode
	 * @return used to chain calls
	 */
	public HashCodeBuilder append(Object... objectValues) {
		for(Object objectValue : objectValues) {
			append(objectValue);
		}
		return this;
	}

	/**
	 * Adds the result of super.hashCode() to this builder.
	 * 
	 * @param superHashCode
	 *            the result of calling super.hashCode()
	 * @return used to chain calls
	 */
	public HashCodeBuilder appendSuper(int superHashCode) {
		delegate.appendSuper(superHashCode);
		return this;
	}

	/**
	 * The generated hash code
	 * 
	 * @return the generated hashcode
	 */
	public int hashCode() {
		return delegate.hashCode();
	}
	
	@Override
	public boolean equals(Object obj) {
		return delegate.equals(obj);
	}
	
	/**
	 * <p>
	 * Uses reflection to build a valid hash code from the fields of object.
	 * </p>
	 * 
	 * <p>
	 * This constructor uses two hard coded choices for the constants needed to
	 * build a hash code.
	 * </p>
	 * 
	 * <p>
	 * It uses AccessibleObject.setAccessible to gain access to private fields.
	 * This means that it will throw a security exception if run under a
	 * security manager, if the permissions are not set up correctly. It is also
	 * not as efficient as testing explicitly.
	 * </p>
	 * 
	 * <p>
	 * Transient members will be not be used, as they are likely derived fields,
	 * and not part of the value of the Object.
	 * </p>
	 * 
	 * <p>
	 * Static fields will not be tested. Superclass fields will be included. If
	 * no fields are found to include in the hash code, the result of this
	 * method will be constant.
	 * </p>
	 * 
	 * @param object
	 *            the Object to create a hashCode for
	 * @param excludeFields
	 *            array of field names to exclude from use in calculation of
	 *            hash code
	 * @return hash code
	 * @throws IllegalArgumentException
	 *             if the object is null
	 */
	public static int reflectionsHashCode(Object object, String... excludeFields) {
		return org.apache.commons.lang3.builder.HashCodeBuilder.reflectionHashCode(object, excludeFields);
	}

}