View Javadoc

1   /*
2    * Copyright 2004 Carlos Sanchez.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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         // Find the leaf class since there may be transients in the leaf 
147         // class or in classes between the leaf and root.
148         // If we are not testing transients or a subclass has no ivars, 
149         // then a subclass can test equals to a superclass.
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                 // rhsClass is a subclass of lhsClass
157                 testClass = rhsClass;
158             }
159         } else if (rhsClass.isInstance(lhs)) {
160             testClass = rhsClass;
161             if (!lhsClass.isInstance(rhs)) {
162                 // lhsClass is a subclass of rhsClass
163                 testClass = lhsClass;
164             }
165         } else {
166             // The two classes are not related.
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             // In this case, we tried to test a subclass vs. a superclass and
178             // the subclass has ivars or the ivars are transient and 
179             // we are testing transients.
180             // If a subclass has ivars that we are trying to test them, we get an
181             // exception and we know that the objects are not equal.
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                     //this can't happen. Would get a Security exception instead
214                     //throw a runtime exception in case the impossible happens.
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             //the simple case, not an array, just test the element
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             //'Switch' on type of array, to dispatch to the correct handler
289             // This handles multi dimensional arrays
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                 // Not an array of primitives
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; //If the types don't match, not equal
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 }