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  
22  import org.apache.commons.lang.builder.EqualsBuilder;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  /***
27   * Customized EqualsBuilder ignoring fields that can't be accessed. Note that
28   * this behaviour can hide errors when testing.
29   * 
30   * Only append(Object, Object) was modified.
31   * 
32   * @see org.apache.commons.lang.builder.EqualsBuilder
33   * 
34   * @author Carlos Sanchez
35   * @author <a href="mailto:steve.downey@netfolio.com">Steve Downey </a>
36   * @author Stephen Colebourne
37   * @author Gary Gregory
38   * @author Pete Gieser
39   * @version $Revision: 1.2 $
40   */
41  public class ExceptionSafeEqualsBuilder extends EqualsBuilder {
42  
43      private static Log log = LogFactory
44              .getLog(ExceptionSafeEqualsBuilder.class);
45  
46      private boolean isEquals;
47  
48      public ExceptionSafeEqualsBuilder() {
49          super();
50          isEquals = true;
51      }
52  
53      //-------------------------------------------------------------------------
54  
55      public static boolean reflectionEquals(Object lhs, Object rhs) {
56          return reflectionEquals(lhs, rhs, false, null);
57      }
58  
59      public static boolean reflectionEquals(Object lhs, Object rhs,
60              boolean testTransients) {
61          return reflectionEquals(lhs, rhs, testTransients, null);
62      }
63  
64      public static boolean reflectionEquals(Object lhs, Object rhs,
65              boolean testTransients, Class reflectUpToClass) {
66          if (lhs == rhs) {
67              return true;
68          }
69          if (lhs == null || rhs == null) {
70              return false;
71          }
72          // Find the leaf class since there may be transients in the leaf
73          // class or in classes between the leaf and root.
74          // If we are not testing transients or a subclass has no ivars,
75          // then a subclass can test equals to a superclass.
76          Class lhsClass = lhs.getClass();
77          Class rhsClass = rhs.getClass();
78          Class testClass;
79          if (lhsClass.isInstance(rhs)) {
80              testClass = lhsClass;
81              if (!rhsClass.isInstance(lhs)) {
82                  // rhsClass is a subclass of lhsClass
83                  testClass = rhsClass;
84              }
85          } else if (rhsClass.isInstance(lhs)) {
86              testClass = rhsClass;
87              if (!lhsClass.isInstance(rhs)) {
88                  // lhsClass is a subclass of rhsClass
89                  testClass = lhsClass;
90              }
91          } else {
92              // The two classes are not related.
93              return false;
94          }
95          ExceptionSafeEqualsBuilder equalsBuilder = new ExceptionSafeEqualsBuilder();
96          try {
97              reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients);
98              while (testClass.getSuperclass() != null
99                      && testClass != reflectUpToClass) {
100                 testClass = testClass.getSuperclass();
101                 reflectionAppend(lhs, rhs, testClass, equalsBuilder,
102                         testTransients);
103             }
104         } catch (IllegalArgumentException e) {
105             // In this case, we tried to test a subclass vs. a superclass and
106             // the subclass has ivars or the ivars are transient and
107             // we are testing transients.
108             // If a subclass has ivars that we are trying to test them, we get
109             // an
110             // exception and we know that the objects are not equal.
111             return false;
112         }
113         return equalsBuilder.isEquals();
114     }
115 
116     private static void reflectionAppend(Object lhs, Object rhs, Class clazz,
117             ExceptionSafeEqualsBuilder builder, boolean useTransients) {
118         Field[] fields = clazz.getDeclaredFields();
119         AccessibleObject.setAccessible(fields, true);
120         for (int i = 0; i < fields.length && builder.isEquals; i++) {
121             Field f = fields[i];
122             if ((f.getName().indexOf('$') == -1)
123                     && (useTransients || !Modifier
124                             .isTransient(f.getModifiers()))
125                     && (!Modifier.isStatic(f.getModifiers()))) {
126                 try {
127                     builder.append(f.get(lhs), f.get(rhs));
128                 } catch (IllegalAccessException e) {
129                     //this can't happen. Would get a Security exception instead
130                     //throw a runtime exception in case the impossible happens.
131                     throw new InternalError("Unexpected IllegalAccessException");
132                 }
133             }
134         }
135     }
136 
137     //-------------------------------------------------------------------------
138 
139     public EqualsBuilder appendSuper(boolean superEquals) {
140         if (isEquals == false) {
141             return this;
142         }
143         isEquals = superEquals;
144         return this;
145     }
146 
147     //-------------------------------------------------------------------------
148 
149     public EqualsBuilder append(Object lhs, Object rhs) {
150         if (isEquals == false) {
151             return this;
152         }
153         if (lhs == rhs) {
154             return this;
155         }
156         if (lhs == null || rhs == null) {
157             if (log.isInfoEnabled()) {
158                 log.info("Equals failed: " + lhs + " is not equal to " + rhs);
159             }
160             isEquals = false;
161             return this;
162         }
163         Class lhsClass = lhs.getClass();
164         if (!lhsClass.isArray()) {
165 
166             // --------------------------------------------------------
167 
168             try {
169 
170                 //the simple case, not an array, just test the element
171                 boolean b = lhs.equals(rhs); 
172                 if (!b && log.isInfoEnabled()) {
173                     log.info("Equals failed: [" + lhs.getClass().getName()
174                             + "] " + lhs + " is not equal to ["
175                             + rhs.getClass().getName() + "] " + rhs);
176                 }
177                 isEquals = b;
178 
179             } catch (Exception e) {
180                 // ignore it
181                 if (log.isInfoEnabled()) {
182                     log.info("Ignored field in equals method due to exception: "
183                         + e.getLocalizedMessage());
184                 }
185             }
186 
187             // --------------------------------------------------------
188 
189         } else {
190             //'Switch' on type of array, to dispatch to the correct handler
191             // This handles multi dimensional arrays
192             if (lhs instanceof long[]) {
193                 append((long[]) lhs, (long[]) rhs);
194             } else if (lhs instanceof int[]) {
195                 append((int[]) lhs, (int[]) rhs);
196             } else if (lhs instanceof short[]) {
197                 append((short[]) lhs, (short[]) rhs);
198             } else if (lhs instanceof char[]) {
199                 append((char[]) lhs, (char[]) rhs);
200             } else if (lhs instanceof byte[]) {
201                 append((byte[]) lhs, (byte[]) rhs);
202             } else if (lhs instanceof double[]) {
203                 append((double[]) lhs, (double[]) rhs);
204             } else if (lhs instanceof float[]) {
205                 append((float[]) lhs, (float[]) rhs);
206             } else if (lhs instanceof boolean[]) {
207                 append((boolean[]) lhs, (boolean[]) rhs);
208             } else {
209                 // Not an array of primitives
210                 append((Object[]) lhs, (Object[]) rhs);
211             }
212         }
213         return this;
214     }
215 
216     public EqualsBuilder append(long lhs, long rhs) {
217         if (isEquals == false) {
218             return this;
219         }
220         isEquals = (lhs == rhs);
221         return this;
222     }
223 
224     public EqualsBuilder append(int lhs, int rhs) {
225         if (isEquals == false) {
226             return this;
227         }
228         isEquals = (lhs == rhs);
229         return this;
230     }
231 
232     public EqualsBuilder append(short lhs, short rhs) {
233         if (isEquals == false) {
234             return this;
235         }
236         isEquals = (lhs == rhs);
237         return this;
238     }
239 
240     public EqualsBuilder append(char lhs, char rhs) {
241         if (isEquals == false) {
242             return this;
243         }
244         isEquals = (lhs == rhs);
245         return this;
246     }
247 
248     public EqualsBuilder append(byte lhs, byte rhs) {
249         if (isEquals == false) {
250             return this;
251         }
252         isEquals = (lhs == rhs);
253         return this;
254     }
255 
256     public EqualsBuilder append(double lhs, double rhs) {
257         if (isEquals == false) {
258             return this;
259         }
260         return append(Double.doubleToLongBits(lhs), Double
261                 .doubleToLongBits(rhs));
262     }
263 
264     public EqualsBuilder append(float lhs, float rhs) {
265         if (isEquals == false) {
266             return this;
267         }
268         return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
269     }
270 
271     public EqualsBuilder append(boolean lhs, boolean rhs) {
272         if (isEquals == false) {
273             return this;
274         }
275         isEquals = (lhs == rhs);
276         return this;
277     }
278 
279     public EqualsBuilder append(Object[] lhs, Object[] rhs) {
280         if (isEquals == false) {
281             return this;
282         }
283         if (lhs == rhs) {
284             return this;
285         }
286         if (lhs == null || rhs == null) {
287             isEquals = false;
288             return this;
289         }
290         if (lhs.length != rhs.length) {
291             isEquals = false;
292             return this;
293         }
294         for (int i = 0; i < lhs.length && isEquals; ++i) {
295             Class lhsClass = lhs[i].getClass();
296             if (!lhsClass.isInstance(rhs[i])) {
297                 isEquals = false; //If the types don't match, not equal
298                 break;
299             }
300             append(lhs[i], rhs[i]);
301         }
302         return this;
303     }
304 
305     public EqualsBuilder append(long[] lhs, long[] rhs) {
306         if (isEquals == false) {
307             return this;
308         }
309         if (lhs == rhs) {
310             return this;
311         }
312         if (lhs == null || rhs == null) {
313             isEquals = false;
314             return this;
315         }
316         if (lhs.length != rhs.length) {
317             isEquals = false;
318             return this;
319         }
320         for (int i = 0; i < lhs.length && isEquals; ++i) {
321             append(lhs[i], rhs[i]);
322         }
323         return this;
324     }
325 
326     public EqualsBuilder append(int[] lhs, int[] rhs) {
327         if (isEquals == false) {
328             return this;
329         }
330         if (lhs == rhs) {
331             return this;
332         }
333         if (lhs == null || rhs == null) {
334             isEquals = false;
335             return this;
336         }
337         if (lhs.length != rhs.length) {
338             isEquals = false;
339             return this;
340         }
341         for (int i = 0; i < lhs.length && isEquals; ++i) {
342             append(lhs[i], rhs[i]);
343         }
344         return this;
345     }
346 
347     public EqualsBuilder append(short[] lhs, short[] rhs) {
348         if (isEquals == false) {
349             return this;
350         }
351         if (lhs == rhs) {
352             return this;
353         }
354         if (lhs == null || rhs == null) {
355             isEquals = false;
356             return this;
357         }
358         if (lhs.length != rhs.length) {
359             isEquals = false;
360             return this;
361         }
362         for (int i = 0; i < lhs.length && isEquals; ++i) {
363             append(lhs[i], rhs[i]);
364         }
365         return this;
366     }
367 
368     public EqualsBuilder append(char[] lhs, char[] rhs) {
369         if (isEquals == false) {
370             return this;
371         }
372         if (lhs == rhs) {
373             return this;
374         }
375         if (lhs == null || rhs == null) {
376             isEquals = false;
377             return this;
378         }
379         if (lhs.length != rhs.length) {
380             isEquals = false;
381             return this;
382         }
383         for (int i = 0; i < lhs.length && isEquals; ++i) {
384             append(lhs[i], rhs[i]);
385         }
386         return this;
387     }
388 
389     public EqualsBuilder append(byte[] lhs, byte[] rhs) {
390         if (isEquals == false) {
391             return this;
392         }
393         if (lhs == rhs) {
394             return this;
395         }
396         if (lhs == null || rhs == null) {
397             isEquals = false;
398             return this;
399         }
400         if (lhs.length != rhs.length) {
401             isEquals = false;
402             return this;
403         }
404         for (int i = 0; i < lhs.length && isEquals; ++i) {
405             append(lhs[i], rhs[i]);
406         }
407         return this;
408     }
409 
410     public EqualsBuilder append(double[] lhs, double[] rhs) {
411         if (isEquals == false) {
412             return this;
413         }
414         if (lhs == rhs) {
415             return this;
416         }
417         if (lhs == null || rhs == null) {
418             isEquals = false;
419             return this;
420         }
421         if (lhs.length != rhs.length) {
422             isEquals = false;
423             return this;
424         }
425         for (int i = 0; i < lhs.length && isEquals; ++i) {
426             append(lhs[i], rhs[i]);
427         }
428         return this;
429     }
430 
431     public EqualsBuilder append(float[] lhs, float[] rhs) {
432         if (isEquals == false) {
433             return this;
434         }
435         if (lhs == rhs) {
436             return this;
437         }
438         if (lhs == null || rhs == null) {
439             isEquals = false;
440             return this;
441         }
442         if (lhs.length != rhs.length) {
443             isEquals = false;
444             return this;
445         }
446         for (int i = 0; i < lhs.length && isEquals; ++i) {
447             append(lhs[i], rhs[i]);
448         }
449         return this;
450     }
451 
452     public EqualsBuilder append(boolean[] lhs, boolean[] rhs) {
453         if (isEquals == false) {
454             return this;
455         }
456         if (lhs == rhs) {
457             return this;
458         }
459         if (lhs == null || rhs == null) {
460             isEquals = false;
461             return this;
462         }
463         if (lhs.length != rhs.length) {
464             isEquals = false;
465             return this;
466         }
467         for (int i = 0; i < lhs.length && isEquals; ++i) {
468             append(lhs[i], rhs[i]);
469         }
470         return this;
471     }
472 
473     public boolean isEquals() {
474         return isEquals;
475     }
476 
477 }