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