1   /* ====================================================================
2    * The Apache Software License, Version 1.1
3    *
4    * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
5    * reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer.
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution, if
20   *    any, must include the following acknowledgement:
21   *       "This product includes software developed by the
22   *        Apache Software Foundation (http://www.apache.org/)."
23   *    Alternately, this acknowledgement may appear in the software itself,
24   *    if and wherever such third-party acknowledgements normally appear.
25   *
26   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
27   *    Foundation" must not be used to endorse or promote products derived
28   *    from this software without prior written permission. For written
29   *    permission, please contact apache@apache.org.
30   *
31   * 5. Products derived from this software may not be called "Apache"
32   *    nor may "Apache" appear in their names without prior written
33   *    permission of the Apache Software Foundation.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46   * SUCH DAMAGE.
47   * ====================================================================
48   *
49   * This software consists of voluntary contributions made by many
50   * individuals on behalf of the Apache Software Foundation.  For more
51   * information on the Apache Software Foundation, please see
52   * <http://www.apache.org/>.
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         // Find the leaf class since there may be transients in the leaf 
217         // class or in classes between the leaf and root.
218         // If we are not testing transients or a subclass has no ivars, 
219         // then a subclass can test equals to a superclass.
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                 // rhsClass is a subclass of lhsClass
227                 testClass = rhsClass;
228             }
229         } else if (rhsClass.isInstance(lhs)) {
230             testClass = rhsClass;
231             if (!lhsClass.isInstance(rhs)) {
232                 // lhsClass is a subclass of rhsClass
233                 testClass = lhsClass;
234             }
235         } else {
236             // The two classes are not related.
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             // In this case, we tried to test a subclass vs. a superclass and
248             // the subclass has ivars or the ivars are transient and 
249             // we are testing transients.
250             // If a subclass has ivars that we are trying to test them, we get an
251             // exception and we know that the objects are not equal.
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                     //this can't happen. Would get a Security exception instead
284                     //throw a runtime exception in case the impossible happens.
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             //the simple case, not an array, just test the element
332             isEquals = TestUtils.equals(lhs, rhs);
333         } else {
334             //'Switch' on type of array, to dispatch to the correct handler
335             // This handles multi dimensional arrays
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                 // Not an array of primitives
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; //If the types don't match, not equal
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 }