HashCodeBuilder.java

1
package fr.sii.ogham.core.util;
2
3
/**
4
 * <p>
5
 * Assists in implementing Object.hashCode() methods.
6
 * </p>
7
 * 
8
 * <p>
9
 * This class enables a good hashCode method to be built for any class. It
10
 * follows the rules laid out in the book Effective Java by Joshua Bloch.
11
 * Writing a good hashCode method is actually quite difficult. This class aims
12
 * to simplify the process.
13
 * </p>
14
 * 
15
 * <p>
16
 * The following is the approach taken. When appending a data field, the current
17
 * total is multiplied by the multiplier then a relevant value for that data
18
 * type is added. For example, if the current hashCode is 17, and the multiplier
19
 * is 37, then appending the integer 45 will create a hashcode of 674, namely 17
20
 * * 37 + 45.
21
 * </p>
22
 * 
23
 * <p>
24
 * All relevant fields from the object should be included in the hashCode
25
 * method. Derived fields may be excluded. In general, any field used in the
26
 * equals method must be used in the hashCode method.
27
 * </p>
28
 * 
29
 * <p>
30
 * To use this class write code as follows:
31
 * </p>
32
 * 
33
 * <pre>
34
 *  public class Person {
35
 *    String name;
36
 *    int age;
37
 *    boolean smoker;
38
 *    ...
39
 * 
40
 *    public int hashCode() {
41
 *      // you pick a hard-coded, randomly chosen, non-zero, odd number
42
 *      // ideally different for each class
43
 *      return new HashCodeBuilder(17, 37).
44
 *        append(name).
45
 *        append(age).
46
 *        append(smoker).
47
 *        hashCode();
48
 *    }
49
 *  }
50
 * </pre>
51
 * <p>
52
 * If required, the superclass hashCode() can be added using appendSuper.
53
 * </p>
54
 * 
55
 * <p>
56
 * Alternatively, there is a method that uses reflection to determine the fields
57
 * to test. Because these fields are usually private, the method,
58
 * reflectionHashCode, uses AccessibleObject.setAccessible to change the
59
 * visibility of the fields. This will fail under a security manager, unless the
60
 * appropriate permissions are set up correctly. It is also slower than testing
61
 * explicitly.
62
 * </p>
63
 * 
64
 * <p>
65
 * A typical invocation for this method would look like:
66
 * </p>
67
 * 
68
 * <pre>
69
 * public int hashCode() {
70
 * 	return HashCodeBuilder.reflectionHashCode(this);
71
 * }
72
 * </pre>
73
 * 
74
 * @author Aurélien Baudet
75
 *
76
 */
77
public class HashCodeBuilder {
78
	private org.apache.commons.lang3.builder.HashCodeBuilder delegate;
79
80
	/**
81
	 * <p>
82
	 * Two randomly chosen, odd numbers must be passed in. Ideally these should
83
	 * be different for each class, however this is not vital.
84
	 * </p>
85
	 * 
86
	 * <p>
87
	 * Prime numbers are preferred, especially for the multiplier.
88
	 * </p>
89
	 * 
90
	 * @param initialOddNumber
91
	 *            an odd number used as the initial value
92
	 * @param multiplierOddNumber
93
	 *            an odd number used as the multiplier
94
	 * @throws IllegalArgumentException
95
	 *             if the number is even
96
	 */
97
	public HashCodeBuilder(int initialOddNumber, int multiplierOddNumber) {
98
		super();
99
		delegate = new org.apache.commons.lang3.builder.HashCodeBuilder(initialOddNumber, multiplierOddNumber);
100
	}
101
102
	/**
103
	 * Uses two hard coded choices for the constants needed to build a hashCode.
104
	 */
105
	public HashCodeBuilder() {
106
		super();
107
		delegate = new org.apache.commons.lang3.builder.HashCodeBuilder();
108
	}
109
110
	/**
111
	 * Append a hashCode for an Object.
112
	 * 
113
	 * @param objectValue
114
	 *            the Object to add to the hashCode
115
	 * @return used to chain calls
116
	 */
117
	public HashCodeBuilder append(Object objectValue) {
118
		delegate.append(objectValue);
119 3 1. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → SURVIVED
2. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → NO_COVERAGE
3. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED
		return this;
120
	}
121
122
	/**
123
	 * Append a hashCode for an Object.
124
	 * 
125
	 * @param objectValues
126
	 *            array of Object to add to the hashCode
127
	 * @return used to chain calls
128
	 */
129
	public HashCodeBuilder append(Object... objectValues) {
130
		for(Object objectValue : objectValues) {
131
			append(objectValue);
132
		}
133 4 1. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → SURVIVED
2. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → NO_COVERAGE
3. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED
4. append : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED
		return this;
134
	}
135
136
	/**
137
	 * Adds the result of super.hashCode() to this builder.
138
	 * 
139
	 * @param superHashCode
140
	 *            the result of calling super.hashCode()
141
	 * @return used to chain calls
142
	 */
143
	public HashCodeBuilder appendSuper(int superHashCode) {
144
		delegate.appendSuper(superHashCode);
145 1 1. appendSuper : replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::appendSuper → NO_COVERAGE
		return this;
146
	}
147
148
	/**
149
	 * The generated hash code
150
	 * 
151
	 * @return the generated hashcode
152
	 */
153
	public int hashCode() {
154 3 1. hashCode : replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → SURVIVED
2. hashCode : replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → NO_COVERAGE
3. hashCode : replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → KILLED
		return delegate.hashCode();
155
	}
156
	
157
	@Override
158
	public boolean equals(Object obj) {
159 2 1. equals : replaced boolean return with false for fr/sii/ogham/core/util/HashCodeBuilder::equals → NO_COVERAGE
2. equals : replaced boolean return with true for fr/sii/ogham/core/util/HashCodeBuilder::equals → NO_COVERAGE
		return delegate.equals(obj);
160
	}
161
	
162
	/**
163
	 * <p>
164
	 * Uses reflection to build a valid hash code from the fields of object.
165
	 * </p>
166
	 * 
167
	 * <p>
168
	 * This constructor uses two hard coded choices for the constants needed to
169
	 * build a hash code.
170
	 * </p>
171
	 * 
172
	 * <p>
173
	 * It uses AccessibleObject.setAccessible to gain access to private fields.
174
	 * This means that it will throw a security exception if run under a
175
	 * security manager, if the permissions are not set up correctly. It is also
176
	 * not as efficient as testing explicitly.
177
	 * </p>
178
	 * 
179
	 * <p>
180
	 * Transient members will be not be used, as they are likely derived fields,
181
	 * and not part of the value of the Object.
182
	 * </p>
183
	 * 
184
	 * <p>
185
	 * Static fields will not be tested. Superclass fields will be included. If
186
	 * no fields are found to include in the hash code, the result of this
187
	 * method will be constant.
188
	 * </p>
189
	 * 
190
	 * @param object
191
	 *            the Object to create a hashCode for
192
	 * @param excludeFields
193
	 *            array of field names to exclude from use in calculation of
194
	 *            hash code
195
	 * @return hash code
196
	 * @throws IllegalArgumentException
197
	 *             if the object is null
198
	 */
199
	public static int reflectionsHashCode(Object object, String... excludeFields) {
200 1 1. reflectionsHashCode : replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::reflectionsHashCode → NO_COVERAGE
		return org.apache.commons.lang3.builder.HashCodeBuilder.reflectionHashCode(object, excludeFields);
201
	}
202
203
}

Mutations

119

1.1
Location : append
Killed by : none
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → SURVIVED

2.2
Location : append
Killed by : none
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → NO_COVERAGE

3.3
Location : append
Killed by : oghamcore.ut.sms.EqualsTest.phoneNumber(oghamcore.ut.sms.EqualsTest)
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED

133

1.1
Location : append
Killed by : oghamall.it.html.translator.JsoupInlineImageTranslatorTest.skipExternalImages(oghamall.it.html.translator.JsoupInlineImageTranslatorTest)
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED

2.2
Location : append
Killed by : none
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → SURVIVED

3.3
Location : append
Killed by : oghamcore.ut.email.EqualsTest.recipient(oghamcore.ut.email.EqualsTest)
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → KILLED

4.4
Location : append
Killed by : none
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::append → NO_COVERAGE

145

1.1
Location : appendSuper
Killed by : none
replaced return value with null for fr/sii/ogham/core/util/HashCodeBuilder::appendSuper → NO_COVERAGE

154

1.1
Location : hashCode
Killed by : none
replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → SURVIVED

2.2
Location : hashCode
Killed by : none
replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → NO_COVERAGE

3.3
Location : hashCode
Killed by : oghamcore.ut.sms.EqualsTest.phoneNumber(oghamcore.ut.sms.EqualsTest)
replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::hashCode → KILLED

159

1.1
Location : equals
Killed by : none
replaced boolean return with false for fr/sii/ogham/core/util/HashCodeBuilder::equals → NO_COVERAGE

2.2
Location : equals
Killed by : none
replaced boolean return with true for fr/sii/ogham/core/util/HashCodeBuilder::equals → NO_COVERAGE

200

1.1
Location : reflectionsHashCode
Killed by : none
replaced int return with 0 for fr/sii/ogham/core/util/HashCodeBuilder::reflectionsHashCode → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT OGHAM