1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 package net.sf.morph.util;
55
56 import java.lang.reflect.AccessibleObject;
57 import java.lang.reflect.Field;
58 import java.lang.reflect.Modifier;
59
60 /**
61 * <p>Assists in implementing {@link Object#equals(Object)} methods.</p>
62 *
63 * <p> This class provides methods to build a good equals method for any
64 * class. It follows rules laid out in
65 * <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
66 * , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>,
67 * <code>floats</code>, and arrays can be tricky. Also, making sure that
68 * <code>equals()</code> and <code>hashCode()</code> are consistent can be
69 * difficult.</p>
70 *
71 * <p>Two Objects that compare as equals must generate the same hash code,
72 * but two Objects with the same hash code do not have to be equal.</p>
73 *
74 * <p>All relevant fields should be included in the calculation of equals.
75 * Derived fields may be ignored. In particular, any field used in
76 * generating a hash code must be used in the equals method, and vice
77 * versa.</p>
78 *
79 * <p>Typical use for the code is as follows:</p>
80 * <pre>
81 * public boolean equals(Object o) {
82 * if ( !(o instanceof MyClass) ) {
83 * return false;
84 * }
85 * MyClass rhs = (MyClass) o;
86 * return new MorphEqualsBuilder()
87 * .appendSuper(super.equals(o))
88 * .append(field1, rhs.field1)
89 * .append(field2, rhs.field2)
90 * .append(field3, rhs.field3)
91 * .isEquals();
92 * }
93 * </pre>
94 *
95 * <p> Alternatively, there is a method that uses reflection to determine
96 * the fields to test. Because these fields are usually private, the method,
97 * <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code> to
98 * change the visibility of the fields. This will fail under a security
99 * manager, unless the appropriate permissions are set up correctly. It is
100 * also slower than testing explicitly.</p>
101 *
102 * <p> A typical invocation for this method would look like:</p>
103 * <pre>
104 * public boolean equals(Object o) {
105 * return MorphEqualsBuilder.reflectionEquals(this, o);
106 * }
107 * </pre>
108 *
109 * <em>This class was copied from Commons-Lang</em>
110 *
111 * @author <a href="mailto:steve.downey@netfolio.com">Steve Downey</a>
112 * @author Stephen Colebourne
113 * @author Gary Gregory
114 * @author Pete Gieser
115 * @author Matthew Sgarlata
116 * @since 1.1
117 * @version $Id: MorphEqualsBuilder.java,v 1.3 2005/01/30 18:33:06 sgarlatm Exp $
118 */
119 public class MorphEqualsBuilder {
120 /**
121 * If the fields tested are equals.
122 */
123 private boolean isEquals;
124
125 /**
126 * <p>Constructor for MorphEqualsBuilder.</p>
127 *
128 * <p>Starts off assuming that equals is <code>true</code>.</p>
129 * @see java.lang.Object#equals
130 */
131 public MorphEqualsBuilder() {
132 super();
133 isEquals = true;
134 }
135
136
137
138 /**
139 * <p>This method uses reflection to determine if the two <code>Object</code>s
140 * are equal.</p>
141 *
142 * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
143 * fields. This means that it will throw a security exception if run under
144 * a security manager, if the permissions are not set up correctly. It is also
145 * not as efficient as testing explicitly.</p>
146 *
147 * <p>Transient members will be not be tested, as they are likely derived
148 * fields, and not part of the value of the Object.</p>
149 *
150 * <p>Static fields will not be tested. Superclass fields will be included.</p>
151 *
152 * @param lhs <code>this</code> object
153 * @param rhs the other object
154 * @return <code>true</code> if the two Objects have tested equals.
155 */
156 public static boolean reflectionEquals(Object lhs, Object rhs) {
157 return reflectionEquals(lhs, rhs, false, null);
158 }
159
160 /**
161 * <p>This method uses reflection to determine if the two <code>Object</code>s
162 * are equal.</p>
163 *
164 * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
165 * fields. This means that it will throw a security exception if run under
166 * a security manager, if the permissions are not set up correctly. It is also
167 * not as efficient as testing explicitly.</p>
168 *
169 * <p>If the TestTransients parameter is set to <code>true</code>, transient
170 * members will be tested, otherwise they are ignored, as they are likely
171 * derived fields, and not part of the value of the <code>Object</code>.</p>
172 *
173 * <p>Static fields will not be tested. Superclass fields will be included.</p>
174 *
175 * @param lhs <code>this</code> object
176 * @param rhs the other object
177 * @param testTransients whether to include transient fields
178 * @return <code>true</code> if the two Objects have tested equals.
179 */
180 public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) {
181 return reflectionEquals(lhs, rhs, testTransients, null);
182 }
183
184 /**
185 * <p>This method uses reflection to determine if the two <code>Object</code>s
186 * are equal.</p>
187 *
188 * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
189 * fields. This means that it will throw a security exception if run under
190 * a security manager, if the permissions are not set up correctly. It is also
191 * not as efficient as testing explicitly.</p>
192 *
193 * <p>If the testTransients parameter is set to <code>true</code>, transient
194 * members will be tested, otherwise they are ignored, as they are likely
195 * derived fields, and not part of the value of the <code>Object</code>.</p>
196 *
197 * <p>Static fields will not be included. Superclass fields will be appended
198 * up to and including the specified superclass. A null superclass is treated
199 * as java.lang.Object.</p>
200 *
201 * @param lhs <code>this</code> object
202 * @param rhs the other object
203 * @param testTransients whether to include transient fields
204 * @param reflectUpToClass the superclass to reflect up to (inclusive),
205 * may be <code>null</code>
206 * @return <code>true</code> if the two Objects have tested equals.
207 * @since 2.0
208 */
209 public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass) {
210 if (lhs == rhs) {
211 return true;
212 }
213 if (lhs == null || rhs == null) {
214 return false;
215 }
216
217
218
219
220 Class lhsClass = lhs.getClass();
221 Class rhsClass = rhs.getClass();
222 Class testClass;
223 if (lhsClass.isInstance(rhs)) {
224 testClass = lhsClass;
225 if (!rhsClass.isInstance(lhs)) {
226
227 testClass = rhsClass;
228 }
229 } else if (rhsClass.isInstance(lhs)) {
230 testClass = rhsClass;
231 if (!lhsClass.isInstance(rhs)) {
232
233 testClass = lhsClass;
234 }
235 } else {
236
237 return false;
238 }
239 MorphEqualsBuilder morphEqualsBuilder = new MorphEqualsBuilder();
240 try {
241 reflectionAppend(lhs, rhs, testClass, morphEqualsBuilder, testTransients);
242 while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
243 testClass = testClass.getSuperclass();
244 reflectionAppend(lhs, rhs, testClass, morphEqualsBuilder, testTransients);
245 }
246 } catch (IllegalArgumentException e) {
247
248
249
250
251
252 return false;
253 }
254 return morphEqualsBuilder.isEquals();
255 }
256
257 /**
258 * <p>Appends the fields and values defined by the given object of the
259 * given Class.</p>
260 *
261 * @param lhs the left hand object
262 * @param rhs the right hand object
263 * @param clazz the class to append details of
264 * @param builder the builder to append to
265 * @param useTransients whether to test transient fields
266 */
267 private static void reflectionAppend(
268 Object lhs,
269 Object rhs,
270 Class clazz,
271 MorphEqualsBuilder builder,
272 boolean useTransients) {
273 Field[] fields = clazz.getDeclaredFields();
274 AccessibleObject.setAccessible(fields, true);
275 for (int i = 0; i < fields.length && builder.isEquals; i++) {
276 Field f = fields[i];
277 if ((f.getName().indexOf('$') == -1)
278 && (useTransients || !Modifier.isTransient(f.getModifiers()))
279 && (!Modifier.isStatic(f.getModifiers()))) {
280 try {
281 builder.append(f.get(lhs), f.get(rhs));
282 } catch (IllegalAccessException e) {
283
284
285 throw new InternalError("Unexpected IllegalAccessException");
286 }
287 }
288 }
289 }
290
291
292
293 /**
294 * <p>Adds the result of <code>super.equals()</code> to this builder.</p>
295 *
296 * @param superEquals the result of calling <code>super.equals()</code>
297 * @return MorphEqualsBuilder - used to chain calls.
298 * @since 2.0
299 */
300 public MorphEqualsBuilder appendSuper(boolean superEquals) {
301 if (isEquals == false) {
302 return this;
303 }
304 isEquals = superEquals;
305 return this;
306 }
307
308
309
310 /**
311 * <p>Test if two <code>Object</code>s are equal using their
312 * <code>equals</code> method.</p>
313 *
314 * @param lhs the left hand object
315 * @param rhs the right hand object
316 * @return MorphEqualsBuilder - used to chain calls.
317 */
318 public MorphEqualsBuilder append(Object lhs, Object rhs) {
319 if (isEquals == false) {
320 return this;
321 }
322 if (lhs == rhs) {
323 return this;
324 }
325 if (lhs == null || rhs == null) {
326 isEquals = false;
327 return this;
328 }
329 Class lhsClass = lhs.getClass();
330 if (!lhsClass.isArray()) {
331
332 isEquals = TestUtils.equals(lhs, rhs);
333 } else {
334
335
336 if (lhs instanceof long[]) {
337 append((long[]) lhs, (long[]) rhs);
338 } else if (lhs instanceof int[]) {
339 append((int[]) lhs, (int[]) rhs);
340 } else if (lhs instanceof short[]) {
341 append((short[]) lhs, (short[]) rhs);
342 } else if (lhs instanceof char[]) {
343 append((char[]) lhs, (char[]) rhs);
344 } else if (lhs instanceof byte[]) {
345 append((byte[]) lhs, (byte[]) rhs);
346 } else if (lhs instanceof double[]) {
347 append((double[]) lhs, (double[]) rhs);
348 } else if (lhs instanceof float[]) {
349 append((float[]) lhs, (float[]) rhs);
350 } else if (lhs instanceof boolean[]) {
351 append((boolean[]) lhs, (boolean[]) rhs);
352 } else {
353
354 append((Object[]) lhs, (Object[]) rhs);
355 }
356 }
357 return this;
358 }
359
360 /**
361 * <p>Test if two <code>long</code>s are equal.</p>
362 *
363 * @param lhs the left hand <code>long</code>
364 * @param rhs the right hand <code>long</code>
365 * @return MorphEqualsBuilder - used to chain calls.
366 */
367 public MorphEqualsBuilder append(long lhs, long rhs) {
368 if (isEquals == false) {
369 return this;
370 }
371 isEquals = (lhs == rhs);
372 return this;
373 }
374
375 /**
376 * <p>Test if two <code>int</code>s are equal.</p>
377 *
378 * @param lhs the left hand <code>int</code>
379 * @param rhs the right hand <code>int</code>
380 * @return MorphEqualsBuilder - used to chain calls.
381 */
382 public MorphEqualsBuilder append(int lhs, int rhs) {
383 if (isEquals == false) {
384 return this;
385 }
386 isEquals = (lhs == rhs);
387 return this;
388 }
389
390 /**
391 * <p>Test if two <code>short</code>s are equal.</p>
392 *
393 * @param lhs the left hand <code>short</code>
394 * @param rhs the right hand <code>short</code>
395 * @return MorphEqualsBuilder - used to chain calls.
396 */
397 public MorphEqualsBuilder append(short lhs, short rhs) {
398 if (isEquals == false) {
399 return this;
400 }
401 isEquals = (lhs == rhs);
402 return this;
403 }
404
405 /**
406 * <p>Test if two <code>char</code>s are equal.</p>
407 *
408 * @param lhs the left hand <code>char</code>
409 * @param rhs the right hand <code>char</code>
410 * @return MorphEqualsBuilder - used to chain calls.
411 */
412 public MorphEqualsBuilder append(char lhs, char rhs) {
413 if (isEquals == false) {
414 return this;
415 }
416 isEquals = (lhs == rhs);
417 return this;
418 }
419
420 /**
421 * <p>Test if two <code>byte</code>s are equal.</p>
422 *
423 * @param lhs the left hand <code>byte</code>
424 * @param rhs the right hand <code>byte</code>
425 * @return MorphEqualsBuilder - used to chain calls.
426 */
427 public MorphEqualsBuilder append(byte lhs, byte rhs) {
428 if (isEquals == false) {
429 return this;
430 }
431 isEquals = (lhs == rhs);
432 return this;
433 }
434
435 /**
436 * <p>Test if two <code>double</code>s are equal by testing that the
437 * pattern of bits returned by <code>doubleToLong</code> are equal.</p>
438 *
439 * <p>This handles NaNs, Infinties, and <code>-0.0</code>.</p>
440 *
441 * <p>It is compatible with the hash code generated by
442 * <code>HashCodeBuilder</code>.</p>
443 *
444 * @param lhs the left hand <code>double</code>
445 * @param rhs the right hand <code>double</code>
446 * @return MorphEqualsBuilder - used to chain calls.
447 */
448 public MorphEqualsBuilder append(double lhs, double rhs) {
449 if (isEquals == false) {
450 return this;
451 }
452 return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
453 }
454
455 /**
456 * <p>Test if two <code>float</code>s are equal byt testing that the
457 * pattern of bits returned by doubleToLong are equal.</p>
458 *
459 * <p>This handles NaNs, Infinties, and <code>-0.0</code>.</p>
460 *
461 * <p>It is compatible with the hash code generated by
462 * <code>HashCodeBuilder</code>.</p>
463 *
464 * @param lhs the left hand <code>float</code>
465 * @param rhs the right hand <code>float</code>
466 * @return MorphEqualsBuilder - used to chain calls.
467 */
468 public MorphEqualsBuilder append(float lhs, float rhs) {
469 if (isEquals == false) {
470 return this;
471 }
472 return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
473 }
474
475 /**
476 * <p>Test if two <code>booleans</code>s are equal.</p>
477 *
478 * @param lhs the left hand <code>boolean</code>
479 * @param rhs the right hand <code>boolean</code>
480 * @return MorphEqualsBuilder - used to chain calls.
481 */
482 public MorphEqualsBuilder append(boolean lhs, boolean rhs) {
483 if (isEquals == false) {
484 return this;
485 }
486 isEquals = (lhs == rhs);
487 return this;
488 }
489
490 /**
491 * <p>Performs a deep comparison of two <code>Object</code> arrays.</p>
492 *
493 * <p>This also will be called for the top level of
494 * multi-dimensional, ragged, and multi-typed arrays.</p>
495 *
496 * @param lhs the left hand <code>Object[]</code>
497 * @param rhs the right hand <code>Object[]</code>
498 * @return MorphEqualsBuilder - used to chain calls.
499 */
500 public MorphEqualsBuilder append(Object[] lhs, Object[] rhs) {
501 if (isEquals == false) {
502 return this;
503 }
504 if (lhs == rhs) {
505 return this;
506 }
507 if (lhs == null || rhs == null) {
508 isEquals = false;
509 return this;
510 }
511 if (lhs.length != rhs.length) {
512 isEquals = false;
513 return this;
514 }
515 for (int i = 0; i < lhs.length && isEquals; ++i) {
516 Class lhsClass = lhs[i].getClass();
517 if (!lhsClass.isInstance(rhs[i])) {
518 isEquals = false;
519 break;
520 }
521 append(lhs[i], rhs[i]);
522 }
523 return this;
524 }
525
526 /**
527 * <p>Deep comparison of array of <code>long</code>. Length and all
528 * values are compared.</p>
529 *
530 * <p>The method {@link #append(long, long)} is used.</p>
531 *
532 * @param lhs the left hand <code>long[]</code>
533 * @param rhs the right hand <code>long[]</code>
534 * @return MorphEqualsBuilder - used to chain calls.
535 */
536 public MorphEqualsBuilder append(long[] lhs, long[] rhs) {
537 if (isEquals == false) {
538 return this;
539 }
540 if (lhs == rhs) {
541 return this;
542 }
543 if (lhs == null || rhs == null) {
544 isEquals = false;
545 return this;
546 }
547 if (lhs.length != rhs.length) {
548 isEquals = false;
549 return this;
550 }
551 for (int i = 0; i < lhs.length && isEquals; ++i) {
552 append(lhs[i], rhs[i]);
553 }
554 return this;
555 }
556
557 /**
558 * <p>Deep comparison of array of <code>int</code>. Length and all
559 * values are compared.</p>
560 *
561 * <p>The method {@link #append(int, int)} is used.</p>
562 *
563 * @param lhs the left hand <code>int[]</code>
564 * @param rhs the right hand <code>int[]</code>
565 * @return MorphEqualsBuilder - used to chain calls.
566 */
567 public MorphEqualsBuilder append(int[] lhs, int[] rhs) {
568 if (isEquals == false) {
569 return this;
570 }
571 if (lhs == rhs) {
572 return this;
573 }
574 if (lhs == null || rhs == null) {
575 isEquals = false;
576 return this;
577 }
578 if (lhs.length != rhs.length) {
579 isEquals = false;
580 return this;
581 }
582 for (int i = 0; i < lhs.length && isEquals; ++i) {
583 append(lhs[i], rhs[i]);
584 }
585 return this;
586 }
587
588 /**
589 * <p>Deep comparison of array of <code>short</code>. Length and all
590 * values are compared.</p>
591 *
592 * <p>The method {@link #append(short, short)} is used.</p>
593 *
594 * @param lhs the left hand <code>short[]</code>
595 * @param rhs the right hand <code>short[]</code>
596 * @return MorphEqualsBuilder - used to chain calls.
597 */
598 public MorphEqualsBuilder append(short[] lhs, short[] rhs) {
599 if (isEquals == false) {
600 return this;
601 }
602 if (lhs == rhs) {
603 return this;
604 }
605 if (lhs == null || rhs == null) {
606 isEquals = false;
607 return this;
608 }
609 if (lhs.length != rhs.length) {
610 isEquals = false;
611 return this;
612 }
613 for (int i = 0; i < lhs.length && isEquals; ++i) {
614 append(lhs[i], rhs[i]);
615 }
616 return this;
617 }
618
619 /**
620 * <p>Deep comparison of array of <code>char</code>. Length and all
621 * values are compared.</p>
622 *
623 * <p>The method {@link #append(char, char)} is used.</p>
624 *
625 * @param lhs the left hand <code>char[]</code>
626 * @param rhs the right hand <code>char[]</code>
627 * @return MorphEqualsBuilder - used to chain calls.
628 */
629 public MorphEqualsBuilder append(char[] lhs, char[] rhs) {
630 if (isEquals == false) {
631 return this;
632 }
633 if (lhs == rhs) {
634 return this;
635 }
636 if (lhs == null || rhs == null) {
637 isEquals = false;
638 return this;
639 }
640 if (lhs.length != rhs.length) {
641 isEquals = false;
642 return this;
643 }
644 for (int i = 0; i < lhs.length && isEquals; ++i) {
645 append(lhs[i], rhs[i]);
646 }
647 return this;
648 }
649
650 /**
651 * <p>Deep comparison of array of <code>byte</code>. Length and all
652 * values are compared.</p>
653 *
654 * <p>The method {@link #append(byte, byte)} is used.</p>
655 *
656 * @param lhs the left hand <code>byte[]</code>
657 * @param rhs the right hand <code>byte[]</code>
658 * @return MorphEqualsBuilder - used to chain calls.
659 */
660 public MorphEqualsBuilder append(byte[] lhs, byte[] rhs) {
661 if (isEquals == false) {
662 return this;
663 }
664 if (lhs == rhs) {
665 return this;
666 }
667 if (lhs == null || rhs == null) {
668 isEquals = false;
669 return this;
670 }
671 if (lhs.length != rhs.length) {
672 isEquals = false;
673 return this;
674 }
675 for (int i = 0; i < lhs.length && isEquals; ++i) {
676 append(lhs[i], rhs[i]);
677 }
678 return this;
679 }
680
681 /**
682 * <p>Deep comparison of array of <code>double</code>. Length and all
683 * values are compared.</p>
684 *
685 * <p>The method {@link #append(double, double)} is used.</p>
686 *
687 * @param lhs the left hand <code>double[]</code>
688 * @param rhs the right hand <code>double[]</code>
689 * @return MorphEqualsBuilder - used to chain calls.
690 */
691 public MorphEqualsBuilder append(double[] lhs, double[] rhs) {
692 if (isEquals == false) {
693 return this;
694 }
695 if (lhs == rhs) {
696 return this;
697 }
698 if (lhs == null || rhs == null) {
699 isEquals = false;
700 return this;
701 }
702 if (lhs.length != rhs.length) {
703 isEquals = false;
704 return this;
705 }
706 for (int i = 0; i < lhs.length && isEquals; ++i) {
707 append(lhs[i], rhs[i]);
708 }
709 return this;
710 }
711
712 /**
713 * <p>Deep comparison of array of <code>float</code>. Length and all
714 * values are compared.</p>
715 *
716 * <p>The method {@link #append(float, float)} is used.</p>
717 *
718 * @param lhs the left hand <code>float[]</code>
719 * @param rhs the right hand <code>float[]</code>
720 * @return MorphEqualsBuilder - used to chain calls.
721 */
722 public MorphEqualsBuilder append(float[] lhs, float[] rhs) {
723 if (isEquals == false) {
724 return this;
725 }
726 if (lhs == rhs) {
727 return this;
728 }
729 if (lhs == null || rhs == null) {
730 isEquals = false;
731 return this;
732 }
733 if (lhs.length != rhs.length) {
734 isEquals = false;
735 return this;
736 }
737 for (int i = 0; i < lhs.length && isEquals; ++i) {
738 append(lhs[i], rhs[i]);
739 }
740 return this;
741 }
742
743 /**
744 * <p>Deep comparison of array of <code>boolean</code>. Length and all
745 * values are compared.</p>
746 *
747 * <p>The method {@link #append(boolean, boolean)} is used.</p>
748 *
749 * @param lhs the left hand <code>boolean[]</code>
750 * @param rhs the right hand <code>boolean[]</code>
751 * @return MorphEqualsBuilder - used to chain calls.
752 */
753 public MorphEqualsBuilder append(boolean[] lhs, boolean[] rhs) {
754 if (isEquals == false) {
755 return this;
756 }
757 if (lhs == rhs) {
758 return this;
759 }
760 if (lhs == null || rhs == null) {
761 isEquals = false;
762 return this;
763 }
764 if (lhs.length != rhs.length) {
765 isEquals = false;
766 return this;
767 }
768 for (int i = 0; i < lhs.length && isEquals; ++i) {
769 append(lhs[i], rhs[i]);
770 }
771 return this;
772 }
773
774 /**
775 * <p>Return <code>true</code> if the fields that have been checked
776 * are all equal.</p>
777 *
778 * @return boolean
779 */
780 public boolean isEquals() {
781 return isEquals;
782 }
783
784 }