directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akaras...@apache.org
Subject svn commit: r151131 [4/9] - in incubator/directory/asn1/branches/rewrite/ber: ./ src/java/org/apache/asn1/ber/ src/java/org/apache/asn1/ber/digester/ src/java/org/apache/asn1/ber/digester/rules/ src/java/org/apache/asn1/ber/primitives/ src/java/org/apache/asn1/tuples/ src/java/org/apache/asn1/util/ src/test/org/apache/asn1/ber/ src/test/org/apache/asn1/ber/primitives/
Date Thu, 03 Feb 2005 07:18:53 GMT
Added: incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/EqualsBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/EqualsBuilder.java?view=auto&rev=151131
==============================================================================
--- incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/EqualsBuilder.java (added)
+++ incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/EqualsBuilder.java Wed Feb  2 23:18:42 2005
@@ -0,0 +1,897 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.asn1.util;
+
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * <p>Assists in implementing {@link Object#equals(Object)} methods.</p>
+ * <p/>
+ * <p> This class provides methods to build a good equals method for any class.
+ * It follows rules laid out in <a href="http://java.sun.com/docs/books/effective/index.html">Effective
+ * Java</a> , by Joshua Bloch. In particular the rule for comparing
+ * <code>doubles</code>, <code>floats</code>, and arrays can be tricky. Also,
+ * making sure that <code>equals()</code> and <code>hashCode()</code> are
+ * consistent can be difficult.</p>
+ * <p/>
+ * <p>Two Objects that compare as equals must generate the same hash code, but
+ * two Objects with the same hash code do not have to be equal.</p>
+ * <p/>
+ * <p>All relevant fields should be included in the calculation of equals.
+ * Derived fields may be ignored. In particular, any field used in generating a
+ * hash code must be used in the equals method, and vice versa.</p>
+ * <p/>
+ * <p>Typical use for the code is as follows:</p>
+ * <pre>
+ * public boolean equals(Object o) {
+ *   if ( !(o instanceof MyClass) ) {
+ *    return false;
+ *   }
+ *  MyClass rhs = (MyClass) o;
+ *  return new EqualsBuilder()
+ *                 .appendSuper(super.equals(o))
+ *                 .append(field1, rhs.field1)
+ *                 .append(field2, rhs.field2)
+ *                 .append(field3, rhs.field3)
+ *                 .isEquals();
+ *  }
+ * </pre>
+ * <p/>
+ * <p> Alternatively, there is a method that uses reflection to determine the
+ * fields to test. Because these fields are usually private, the method,
+ * <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code>
+ * to change the visibility of the fields. This will fail under a security
+ * manager, unless the appropriate permissions are set up correctly. It is also
+ * slower than testing explicitly.</p>
+ * <p/>
+ * <p> A typical invocation for this method would look like:</p>
+ * <pre>
+ * public boolean equals(Object o) {
+ *   return EqualsBuilder.reflectionEquals(this, o);
+ * }
+ * </pre>
+ *
+ * @author <a href="mailto:steve.downey@netfolio.com">Steve Downey</a>
+ * @author Stephen Colebourne
+ * @author Gary Gregory
+ * @author Pete Gieser
+ * @author Arun Mammen Thomas
+ * @version $Id: EqualsBuilder.java,v 1.26 2004/08/26 05:46:45 ggregory Exp $
+ * @since 1.0
+ */
+public class EqualsBuilder
+{
+
+    /**
+     * If the fields tested are equals. The default value is <code>true</code>.
+     */
+    private boolean isEquals = true;
+
+
+    /**
+     * <p>Constructor for EqualsBuilder.</p>
+     * <p/>
+     * <p>Starts off assuming that equals is <code>true</code>.</p>
+     *
+     * @see Object#equals(Object)
+     */
+    public EqualsBuilder()
+    {
+        // do nothing for now.
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>This method uses reflection to determine if the two
+     * <code>Object</code>s are equal.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>Transient members will be not be tested, as they are likely derived
+     * fields, and not part of the value of the Object.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     *
+     * @param lhs <code>this</code> object
+     * @param rhs the other object
+     * @return <code>true</code> if the two Objects have tested equals.
+     */
+    public static boolean reflectionEquals( Object lhs, Object rhs )
+    {
+        return reflectionEquals( lhs, rhs, false, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to determine if the two
+     * <code>Object</code>s are equal.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>If the TestTransients parameter is set to <code>true</code>, transient
+     * members will be tested, otherwise they are ignored, as they are likely
+     * derived fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     *
+     * @param lhs            <code>this</code> object
+     * @param rhs            the other object
+     * @param testTransients whether to include transient fields
+     * @return <code>true</code> if the two Objects have tested equals.
+     */
+    public static boolean reflectionEquals( Object lhs, Object rhs, boolean testTransients )
+    {
+        return reflectionEquals( lhs, rhs, testTransients, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to determine if the two
+     * <code>Object</code>s are equal.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>If the testTransients parameter is set to <code>true</code>, transient
+     * members will be tested, otherwise they are ignored, as they are likely
+     * derived fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be included. Superclass fields will be appended
+     * up to and including the specified superclass. A null superclass is
+     * treated as java.lang.Object.</p>
+     *
+     * @param lhs              <code>this</code> object
+     * @param rhs              the other object
+     * @param testTransients   whether to include transient fields
+     * @param reflectUpToClass the superclass to reflect up to (inclusive), may
+     *                         be <code>null</code>
+     * @return <code>true</code> if the two Objects have tested equals.
+     * @since 2.0
+     */
+    public static boolean reflectionEquals( Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass )
+    {
+        if ( lhs == rhs )
+        {
+            return true;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            return false;
+        }
+        // Find the leaf class since there may be transients in the leaf
+        // class or in classes between the leaf and root.
+        // If we are not testing transients or a subclass has no ivars,
+        // then a subclass can test equals to a superclass.
+        Class lhsClass = lhs.getClass();
+        Class rhsClass = rhs.getClass();
+        Class testClass;
+        if ( lhsClass.isInstance( rhs ) )
+        {
+            testClass = lhsClass;
+            if ( !rhsClass.isInstance( lhs ) )
+            {
+                // rhsClass is a subclass of lhsClass
+                testClass = rhsClass;
+            }
+        }
+        else if ( rhsClass.isInstance( lhs ) )
+        {
+            testClass = rhsClass;
+            if ( !lhsClass.isInstance( rhs ) )
+            {
+                // lhsClass is a subclass of rhsClass
+                testClass = lhsClass;
+            }
+        }
+        else
+        {
+            // The two classes are not related.
+            return false;
+        }
+        EqualsBuilder equalsBuilder = new EqualsBuilder();
+        try
+        {
+            reflectionAppend( lhs, rhs, testClass, equalsBuilder, testTransients );
+            while ( testClass.getSuperclass() != null && testClass != reflectUpToClass )
+            {
+                testClass = testClass.getSuperclass();
+                reflectionAppend( lhs, rhs, testClass, equalsBuilder, testTransients );
+            }
+        }
+        catch ( IllegalArgumentException e )
+        {
+            // In this case, we tried to test a subclass vs. a superclass and
+            // the subclass has ivars or the ivars are transient and
+            // we are testing transients.
+            // If a subclass has ivars that we are trying to test them, we get an
+            // exception and we know that the objects are not equal.
+            return false;
+        }
+        return equalsBuilder.isEquals();
+    }
+
+
+    /**
+     * <p>Appends the fields and values defined by the given object of the given
+     * Class.</p>
+     *
+     * @param lhs           the left hand object
+     * @param rhs           the right hand object
+     * @param clazz         the class to append details of
+     * @param builder       the builder to append to
+     * @param useTransients whether to test transient fields
+     */
+    private static void reflectionAppend( Object lhs,
+                                          Object rhs,
+                                          Class clazz,
+                                          EqualsBuilder builder,
+                                          boolean useTransients )
+    {
+        Field[] fields = clazz.getDeclaredFields();
+        AccessibleObject.setAccessible( fields, true );
+        for ( int i = 0; i < fields.length && builder.isEquals; i++ )
+        {
+            Field f = fields[i];
+            if ( ( f.getName().indexOf( '$' ) == -1 )
+                    && ( useTransients || !Modifier.isTransient( f.getModifiers() ) )
+                    && ( !Modifier.isStatic( f.getModifiers() ) ) )
+            {
+                try
+                {
+                    builder.append( f.get( lhs ), f.get( rhs ) );
+                }
+                catch ( IllegalAccessException e )
+                {
+                    //this can't happen. Would get a Security exception instead
+                    //throw a runtime exception in case the impossible happens.
+                    throw new InternalError( "Unexpected IllegalAccessException" );
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>Adds the result of <code>super.equals()</code> to this builder.</p>
+     *
+     * @param superEquals the result of calling <code>super.equals()</code>
+     * @return EqualsBuilder - used to chain calls.
+     * @since 2.0
+     */
+    public EqualsBuilder appendSuper( boolean superEquals )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = superEquals;
+        return this;
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>Test if two <code>Object</code>s are equal using their
+     * <code>equals</code> method.</p>
+     *
+     * @param lhs the left hand object
+     * @param rhs the right hand object
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( Object lhs, Object rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        Class lhsClass = lhs.getClass();
+        if ( !lhsClass.isArray() )
+        {
+            // The simple case, not an array, just test the element
+            isEquals = lhs.equals( rhs );
+        }
+        else if ( lhs.getClass() != rhs.getClass() )
+        {
+            // Here when we compare different dimensions, for example: a boolean[][] to a boolean[]
+            this.setEquals( false );
+        }
+        // 'Switch' on type of array, to dispatch to the correct handler
+        // This handles multi dimensional arrays of the same depth
+        else if ( lhs instanceof long[] )
+        {
+            append( ( long[] ) lhs, ( long[] ) rhs );
+        }
+        else if ( lhs instanceof int[] )
+        {
+            append( ( int[] ) lhs, ( int[] ) rhs );
+        }
+        else if ( lhs instanceof short[] )
+        {
+            append( ( short[] ) lhs, ( short[] ) rhs );
+        }
+        else if ( lhs instanceof char[] )
+        {
+            append( ( char[] ) lhs, ( char[] ) rhs );
+        }
+        else if ( lhs instanceof byte[] )
+        {
+            append( ( byte[] ) lhs, ( byte[] ) rhs );
+        }
+        else if ( lhs instanceof double[] )
+        {
+            append( ( double[] ) lhs, ( double[] ) rhs );
+        }
+        else if ( lhs instanceof float[] )
+        {
+            append( ( float[] ) lhs, ( float[] ) rhs );
+        }
+        else if ( lhs instanceof boolean[] )
+        {
+            append( ( boolean[] ) lhs, ( boolean[] ) rhs );
+        }
+        else
+        {
+            // Not an array of primitives
+            append( ( Object[] ) lhs, ( Object[] ) rhs );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p/>
+     * Test if two <code>long</code> s are equal. </p>
+     *
+     * @param lhs the left hand <code>long</code>
+     * @param rhs the right hand <code>long</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( long lhs, long rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Test if two <code>int</code>s are equal.</p>
+     *
+     * @param lhs the left hand <code>int</code>
+     * @param rhs the right hand <code>int</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( int lhs, int rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Test if two <code>short</code>s are equal.</p>
+     *
+     * @param lhs the left hand <code>short</code>
+     * @param rhs the right hand <code>short</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( short lhs, short rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Test if two <code>char</code>s are equal.</p>
+     *
+     * @param lhs the left hand <code>char</code>
+     * @param rhs the right hand <code>char</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( char lhs, char rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Test if two <code>byte</code>s are equal.</p>
+     *
+     * @param lhs the left hand <code>byte</code>
+     * @param rhs the right hand <code>byte</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( byte lhs, byte rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Test if two <code>double</code>s are equal by testing that the pattern
+     * of bits returned by <code>doubleToLong</code> are equal.</p>
+     * <p/>
+     * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+     * <p/>
+     * <p>It is compatible with the hash code generated by
+     * <code>HashCodeBuilder</code>.</p>
+     *
+     * @param lhs the left hand <code>double</code>
+     * @param rhs the right hand <code>double</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( double lhs, double rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        return append( Double.doubleToLongBits( lhs ), Double.doubleToLongBits( rhs ) );
+    }
+
+
+    /**
+     * <p>Test if two <code>float</code>s are equal byt testing that the pattern
+     * of bits returned by doubleToLong are equal.</p>
+     * <p/>
+     * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
+     * <p/>
+     * <p>It is compatible with the hash code generated by
+     * <code>HashCodeBuilder</code>.</p>
+     *
+     * @param lhs the left hand <code>float</code>
+     * @param rhs the right hand <code>float</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( float lhs, float rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        return append( Float.floatToIntBits( lhs ), Float.floatToIntBits( rhs ) );
+    }
+
+
+    /**
+     * <p>Test if two <code>booleans</code>s are equal.</p>
+     *
+     * @param lhs the left hand <code>boolean</code>
+     * @param rhs the right hand <code>boolean</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( boolean lhs, boolean rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        isEquals = ( lhs == rhs );
+        return this;
+    }
+
+
+    /**
+     * <p>Performs a deep comparison of two <code>Object</code> arrays.</p>
+     * <p/>
+     * <p>This also will be called for the top level of multi-dimensional,
+     * ragged, and multi-typed arrays.</p>
+     *
+     * @param lhs the left hand <code>Object[]</code>
+     * @param rhs the right hand <code>Object[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( Object[] lhs, Object[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>long</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(long, long)} is used.</p>
+     *
+     * @param lhs the left hand <code>long[]</code>
+     * @param rhs the right hand <code>long[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( long[] lhs, long[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>int</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(int, int)} is used.</p>
+     *
+     * @param lhs the left hand <code>int[]</code>
+     * @param rhs the right hand <code>int[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( int[] lhs, int[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>short</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(short, short)} is used.</p>
+     *
+     * @param lhs the left hand <code>short[]</code>
+     * @param rhs the right hand <code>short[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( short[] lhs, short[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>char</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(char, char)} is used.</p>
+     *
+     * @param lhs the left hand <code>char[]</code>
+     * @param rhs the right hand <code>char[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( char[] lhs, char[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>byte</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(byte, byte)} is used.</p>
+     *
+     * @param lhs the left hand <code>byte[]</code>
+     * @param rhs the right hand <code>byte[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( byte[] lhs, byte[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>double</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(double, double)} is used.</p>
+     *
+     * @param lhs the left hand <code>double[]</code>
+     * @param rhs the right hand <code>double[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( double[] lhs, double[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>float</code>. Length and all values
+     * are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(float, float)} is used.</p>
+     *
+     * @param lhs the left hand <code>float[]</code>
+     * @param rhs the right hand <code>float[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( float[] lhs, float[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Deep comparison of array of <code>boolean</code>. Length and all
+     * values are compared.</p>
+     * <p/>
+     * <p>The method {@link #append(boolean, boolean)} is used.</p>
+     *
+     * @param lhs the left hand <code>boolean[]</code>
+     * @param rhs the right hand <code>boolean[]</code>
+     * @return EqualsBuilder - used to chain calls.
+     */
+    public EqualsBuilder append( boolean[] lhs, boolean[] rhs )
+    {
+        if ( isEquals == false )
+        {
+            return this;
+        }
+        if ( lhs == rhs )
+        {
+            return this;
+        }
+        if ( lhs == null || rhs == null )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        if ( lhs.length != rhs.length )
+        {
+            this.setEquals( false );
+            return this;
+        }
+        for ( int i = 0; i < lhs.length && isEquals; ++i )
+        {
+            append( lhs[i], rhs[i] );
+        }
+        return this;
+    }
+
+
+    /**
+     * <p>Returns <code>true</code> if the fields that have been checked are all
+     * equal.</p>
+     *
+     * @return boolean
+     */
+    public boolean isEquals()
+    {
+        return this.isEquals;
+    }
+
+
+    /**
+     * Sets the <code>isEquals</code> value.
+     *
+     * @param isEquals The value to set.
+     */
+    protected void setEquals( boolean isEquals )
+    {
+        this.isEquals = isEquals;
+    }
+}

Added: incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/ExceptionUtils.java
URL: http://svn.apache.org/viewcvs/incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/ExceptionUtils.java?view=auto&rev=151131
==============================================================================
--- incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/ExceptionUtils.java (added)
+++ incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/ExceptionUtils.java Wed Feb  2 23:18:42 2005
@@ -0,0 +1,815 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.asn1.util;
+
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.*;
+
+
+/**
+ * <p>Provides utilities for manipulating and examining <code>Throwable</code>
+ * objects.</p>
+ *
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory
+ *         Project</a>
+ * @version $Rev: 56542 $
+ */
+public class ExceptionUtils
+{
+
+    /**
+     * <p>Used when printing stack frames to denote the start of a wrapped
+     * exception.</p>
+     * <p/>
+     * <p>Package private for accessibility by test suite.</p>
+     */
+    static final String WRAPPED_MARKER = " [wrapped] ";
+
+    /**
+     * <p>The names of methods commonly used to access a wrapped exception.</p>
+     */
+    private static String[] CAUSE_METHOD_NAMES = {
+        "getCause",
+        "getNextException",
+        "getTargetException",
+        "getException",
+        "getSourceException",
+        "getRootCause",
+        "getCausedByException",
+        "getNested",
+        "getLinkedException",
+        "getNestedException",
+        "getLinkedCause",
+        "getThrowable",
+    };
+
+    /**
+     * <p>The Method object for JDK1.4 getCause.</p>
+     */
+    private static final Method THROWABLE_CAUSE_METHOD;
+
+
+    static
+    {
+        Method getCauseMethod;
+        try
+        {
+            getCauseMethod = Throwable.class.getMethod( "getCause", null );
+        }
+        catch ( Exception e )
+        {
+            getCauseMethod = null;
+        }
+        THROWABLE_CAUSE_METHOD = getCauseMethod;
+    }
+
+
+    /**
+     * <p>Public constructor allows an instance of <code>ExceptionUtils</code>
+     * to be created, although that is not normally necessary.</p>
+     */
+    public ExceptionUtils()
+    {
+    }
+
+
+    /**
+     * <p>Checks if a String is not empty ("") and not null.</p>
+     *
+     * @param str the String to check, may be null
+     * @return <code>true</code> if the String is not empty and not null
+     */
+    private static boolean isNotEmpty( String str )
+    {
+        return ( str != null && str.length() > 0 );
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Adds to the list of method names used in the search for
+     * <code>Throwable</code> objects.</p>
+     *
+     * @param methodName the methodName to add to the list, <code>null</code>
+     *                   and empty strings are ignored
+     * @since 2.0
+     */
+    public static void addCauseMethodName( String methodName )
+    {
+        if ( isNotEmpty( methodName ) )
+        {
+            List list = new ArrayList( Arrays.asList( CAUSE_METHOD_NAMES ) );
+            list.add( methodName );
+            CAUSE_METHOD_NAMES = ( String[] ) list.toArray( new String[list.size()] );
+        }
+    }
+
+
+    /**
+     * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
+     * <p/>
+     * <p>The method searches for methods with specific names that return a
+     * <code>Throwable</code> object. This will pick up most wrapping
+     * exceptions, including those from JDK 1.4, and The method names can be
+     * added to using {@link #addCauseMethodName(String)}.</p>
+     * <p/>
+     * <p>The default list searched for are:</p> <ul> <li><code>getCause()</code></li>
+     * <li><code>getNextException()</code></li> <li><code>getTargetException()</code></li>
+     * <li><code>getException()</code></li> <li><code>getSourceException()</code></li>
+     * <li><code>getRootCause()</code></li> <li><code>getCausedByException()</code></li>
+     * <li><code>getNested()</code></li> </ul>
+     * <p/>
+     * <p>In the absence of any such method, the object is inspected for a
+     * <code>detail</code> field assignable to a <code>Throwable</code>.</p>
+     * <p/>
+     * <p>If none of the above is found, returns <code>null</code>.</p>
+     *
+     * @param throwable the throwable to introspect for a cause, may be null
+     * @return the cause of the <code>Throwable</code>, <code>null</code> if
+     *         none found or null throwable input
+     * @since 1.0
+     */
+    public static Throwable getCause( Throwable throwable )
+    {
+        return getCause( throwable, CAUSE_METHOD_NAMES );
+    }
+
+
+    /**
+     * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
+     * <p/>
+     * <ol> <li>Try known exception types.</li> <li>Try the supplied array of
+     * method names.</li> <li>Try the field 'detail'.</li> </ol>
+     * <p/>
+     * <p>A <code>null</code> set of method names means use the default set. A
+     * <code>null</code> in the set of method names will be ignored.</p>
+     *
+     * @param throwable   the throwable to introspect for a cause, may be null
+     * @param methodNames the method names, null treated as default set
+     * @return the cause of the <code>Throwable</code>, <code>null</code> if
+     *         none found or null throwable input
+     * @since 1.0
+     */
+    public static Throwable getCause( Throwable throwable, String[] methodNames )
+    {
+        if ( throwable == null )
+        {
+            return null;
+        }
+        Throwable cause = getCauseUsingWellKnownTypes( throwable );
+        if ( cause == null )
+        {
+            if ( methodNames == null )
+            {
+                methodNames = CAUSE_METHOD_NAMES;
+            }
+            for ( int i = 0; i < methodNames.length; i++ )
+            {
+                String methodName = methodNames[i];
+                if ( methodName != null )
+                {
+                    cause = getCauseUsingMethodName( throwable, methodName );
+                    if ( cause != null )
+                    {
+                        break;
+                    }
+                }
+            }
+
+            if ( cause == null )
+            {
+                cause = getCauseUsingFieldName( throwable, "detail" );
+            }
+        }
+        return cause;
+    }
+
+
+    /**
+     * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
+     * <p/>
+     * <p>This method walks through the exception chain to the last element,
+     * "root" of the tree, using {@link #getCause(Throwable)}, and returns that
+     * exception.</p>
+     *
+     * @param throwable the throwable to get the root cause for, may be null
+     * @return the root cause of the <code>Throwable</code>, <code>null</code>
+     *         if none found or null throwable input
+     */
+    public static Throwable getRootCause( Throwable throwable )
+    {
+        Throwable cause = getCause( throwable );
+        if ( cause != null )
+        {
+            throwable = cause;
+            while ( ( throwable = getCause( throwable ) ) != null )
+            {
+                cause = throwable;
+            }
+        }
+        return cause;
+    }
+
+
+    /**
+     * <p>Finds a <code>Throwable</code> for known types.</p>
+     * <p/>
+     * <p>Uses <code>instanceof</code> checks to examine the exception, looking
+     * for well known types which could contain chained or wrapped
+     * exceptions.</p>
+     *
+     * @param throwable the exception to examine
+     * @return the wrapped exception, or <code>null</code> if not found
+     */
+    private static Throwable getCauseUsingWellKnownTypes( Throwable throwable )
+    {
+        if ( throwable instanceof Nestable )
+        {
+            return ( ( Nestable ) throwable ).getCause();
+        }
+        else if ( throwable instanceof SQLException )
+        {
+            return ( ( SQLException ) throwable ).getNextException();
+        }
+        else if ( throwable instanceof InvocationTargetException )
+        {
+            return ( ( InvocationTargetException ) throwable ).getTargetException();
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * <p>Finds a <code>Throwable</code> by method name.</p>
+     *
+     * @param throwable  the exception to examine
+     * @param methodName the name of the method to find and invoke
+     * @return the wrapped exception, or <code>null</code> if not found
+     */
+    private static Throwable getCauseUsingMethodName( Throwable throwable, String methodName )
+    {
+        Method method = null;
+        try
+        {
+            method = throwable.getClass().getMethod( methodName, null );
+        }
+        catch ( NoSuchMethodException ignored )
+        {
+        }
+        catch ( SecurityException ignored )
+        {
+        }
+
+        if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) )
+        {
+            try
+            {
+                return ( Throwable ) method.invoke( throwable, ArrayUtils.EMPTY_OBJECT_ARRAY );
+            }
+            catch ( IllegalAccessException ignored )
+            {
+            }
+            catch ( IllegalArgumentException ignored )
+            {
+            }
+            catch ( InvocationTargetException ignored )
+            {
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * <p>Finds a <code>Throwable</code> by field name.</p>
+     *
+     * @param throwable the exception to examine
+     * @param fieldName the name of the attribute to examine
+     * @return the wrapped exception, or <code>null</code> if not found
+     */
+    private static Throwable getCauseUsingFieldName( Throwable throwable, String fieldName )
+    {
+        Field field = null;
+        try
+        {
+            field = throwable.getClass().getField( fieldName );
+        }
+        catch ( NoSuchFieldException ignored )
+        {
+        }
+        catch ( SecurityException ignored )
+        {
+        }
+
+        if ( field != null && Throwable.class.isAssignableFrom( field.getType() ) )
+        {
+            try
+            {
+                return ( Throwable ) field.get( throwable );
+            }
+            catch ( IllegalAccessException ignored )
+            {
+            }
+            catch ( IllegalArgumentException ignored )
+            {
+            }
+        }
+        return null;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
+     * <p/>
+     * <p>This is true for JDK 1.4 and above.</p>
+     *
+     * @return true if Throwable is nestable
+     * @since 2.0
+     */
+    public static boolean isThrowableNested()
+    {
+        return ( THROWABLE_CAUSE_METHOD != null );
+    }
+
+
+    /**
+     * <p>Checks whether this <code>Throwable</code> class can store a
+     * cause.</p>
+     * <p/>
+     * <p>This method does <b>not</b> check whether it actually does store a
+     * cause.<p>
+     *
+     * @param throwable the <code>Throwable</code> to examine, may be null
+     * @return boolean <code>true</code> if nested otherwise <code>false</code>
+     * @since 2.0
+     */
+    public static boolean isNestedThrowable( Throwable throwable )
+    {
+        if ( throwable == null )
+        {
+            return false;
+        }
+
+        if ( throwable instanceof Nestable )
+        {
+            return true;
+        }
+        else if ( throwable instanceof SQLException )
+        {
+            return true;
+        }
+        else if ( throwable instanceof InvocationTargetException )
+        {
+            return true;
+        }
+        else if ( isThrowableNested() )
+        {
+            return true;
+        }
+
+        Class cls = throwable.getClass();
+        for ( int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++ )
+        {
+            try
+            {
+                Method method = cls.getMethod( CAUSE_METHOD_NAMES[i], null );
+                if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) )
+                {
+                    return true;
+                }
+            }
+            catch ( NoSuchMethodException ignored )
+            {
+            }
+            catch ( SecurityException ignored )
+            {
+            }
+        }
+
+        try
+        {
+            Field field = cls.getField( "detail" );
+            if ( field != null )
+            {
+                return true;
+            }
+        }
+        catch ( NoSuchFieldException ignored )
+        {
+        }
+        catch ( SecurityException ignored )
+        {
+        }
+
+        return false;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Counts the number of <code>Throwable</code> objects in the exception
+     * chain.</p>
+     * <p/>
+     * <p>A throwable without cause will return <code>1</code>. A throwable with
+     * one cause will return <code>2</code> and so on. A <code>null</code>
+     * throwable will return <code>0</code>.</p>
+     *
+     * @param throwable the throwable to inspect, may be null
+     * @return the count of throwables, zero if null input
+     */
+    public static int getThrowableCount( Throwable throwable )
+    {
+        int count = 0;
+        while ( throwable != null )
+        {
+            count++;
+            throwable = ExceptionUtils.getCause( throwable );
+        }
+        return count;
+    }
+
+
+    /**
+     * <p>Returns the list of <code>Throwable</code> objects in the exception
+     * chain.</p>
+     * <p/>
+     * <p>A throwable without cause will return an array containing one element
+     * - the input throwable. A throwable with one cause will return an array
+     * containing two elements. - the input throwable and the cause throwable. A
+     * <code>null</code> throwable will return an array size zero.</p>
+     *
+     * @param throwable the throwable to inspect, may be null
+     * @return the array of throwables, never null
+     */
+    public static Throwable[] getThrowables( Throwable throwable )
+    {
+        List list = new ArrayList();
+        while ( throwable != null )
+        {
+            list.add( throwable );
+            throwable = ExceptionUtils.getCause( throwable );
+        }
+        return ( Throwable[] ) list.toArray( new Throwable[list.size()] );
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Returns the (zero based) index of the first <code>Throwable</code>
+     * that matches the specified type in the exception chain.</p>
+     * <p/>
+     * <p>A <code>null</code> throwable returns <code>-1</code>. A
+     * <code>null</code> type returns <code>-1</code>. No match in the chain
+     * returns <code>-1</code>.</p>
+     *
+     * @param throwable the throwable to inspect, may be null
+     * @param type      the type to search for
+     * @return the index into the throwable chain, -1 if no match or null input
+     */
+    public static int indexOfThrowable( Throwable throwable, Class type )
+    {
+        return indexOfThrowable( throwable, type, 0 );
+    }
+
+
+    /**
+     * <p>Returns the (zero based) index of the first <code>Throwable</code>
+     * that matches the specified type in the exception chain from a specified
+     * index.</p>
+     * <p/>
+     * <p>A <code>null</code> throwable returns <code>-1</code>. A
+     * <code>null</code> type returns <code>-1</code>. No match in the chain
+     * returns <code>-1</code>. A negative start index is treated as zero. A
+     * start index greater than the number of throwables returns
+     * <code>-1</code>.</p>
+     *
+     * @param throwable the throwable to inspect, may be null
+     * @param type      the type to search for
+     * @param fromIndex the (zero based) index of the starting position,
+     *                  negative treated as zero, larger than chain size returns
+     *                  -1
+     * @return the index into the throwable chain, -1 if no match or null input
+     */
+    public static int indexOfThrowable( Throwable throwable, Class type, int fromIndex )
+    {
+        if ( throwable == null )
+        {
+            return -1;
+        }
+        if ( fromIndex < 0 )
+        {
+            fromIndex = 0;
+        }
+        Throwable[] throwables = ExceptionUtils.getThrowables( throwable );
+        if ( fromIndex >= throwables.length )
+        {
+            return -1;
+        }
+        for ( int i = fromIndex; i < throwables.length; i++ )
+        {
+            if ( throwables[i].getClass().equals( type ) )
+            {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Prints a compact stack trace for the root cause of a throwable to
+     * <code>System.err</code>.</p>
+     * <p/>
+     * <p>The compact stack trace starts with the root cause and prints stack
+     * frames up to the place where it was caught and wrapped. Then it prints
+     * the wrapped exception and continues with stack frames until the wrapper
+     * exception is caught and wrapped again, etc.</p>
+     * <p/>
+     * <p>The method is equivalent to <code>printStackTrace</code> for
+     * throwables that don't have nested causes.</p>
+     *
+     * @param throwable the throwable to output
+     * @since 2.0
+     */
+    public static void printRootCauseStackTrace( Throwable throwable )
+    {
+        printRootCauseStackTrace( throwable, System.err );
+    }
+
+
+    /**
+     * <p>Prints a compact stack trace for the root cause of a throwable.</p>
+     * <p/>
+     * <p>The compact stack trace starts with the root cause and prints stack
+     * frames up to the place where it was caught and wrapped. Then it prints
+     * the wrapped exception and continues with stack frames until the wrapper
+     * exception is caught and wrapped again, etc.</p>
+     * <p/>
+     * <p>The method is equivalent to <code>printStackTrace</code> for
+     * throwables that don't have nested causes.</p>
+     *
+     * @param throwable the throwable to output, may be null
+     * @param stream    the stream to output to, may not be null
+     * @throws IllegalArgumentException if the stream is <code>null</code>
+     * @since 2.0
+     */
+    public static void printRootCauseStackTrace( Throwable throwable, PrintStream stream )
+    {
+        if ( throwable == null )
+        {
+            return;
+        }
+        if ( stream == null )
+        {
+            throw new IllegalArgumentException( "The PrintStream must not be null" );
+        }
+        String trace[] = getRootCauseStackTrace( throwable );
+        for ( int i = 0; i < trace.length; i++ )
+        {
+            stream.println( trace[i] );
+        }
+        stream.flush();
+    }
+
+
+    /**
+     * <p>Prints a compact stack trace for the root cause of a throwable.</p>
+     * <p/>
+     * <p>The compact stack trace starts with the root cause and prints stack
+     * frames up to the place where it was caught and wrapped. Then it prints
+     * the wrapped exception and continues with stack frames until the wrapper
+     * exception is caught and wrapped again, etc.</p>
+     * <p/>
+     * <p>The method is equivalent to <code>printStackTrace</code> for
+     * throwables that don't have nested causes.</p>
+     *
+     * @param throwable the throwable to output, may be null
+     * @param writer    the writer to output to, may not be null
+     * @throws IllegalArgumentException if the writer is <code>null</code>
+     * @since 2.0
+     */
+    public static void printRootCauseStackTrace( Throwable throwable, PrintWriter writer )
+    {
+        if ( throwable == null )
+        {
+            return;
+        }
+        if ( writer == null )
+        {
+            throw new IllegalArgumentException( "The PrintWriter must not be null" );
+        }
+        String trace[] = getRootCauseStackTrace( throwable );
+        for ( int i = 0; i < trace.length; i++ )
+        {
+            writer.println( trace[i] );
+        }
+        writer.flush();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Creates a compact stack trace for the root cause of the supplied
+     * <code>Throwable</code>.</p>
+     *
+     * @param throwable the throwable to examine, may be null
+     * @return an array of stack trace frames, never null
+     * @since 2.0
+     */
+    public static String[] getRootCauseStackTrace( Throwable throwable )
+    {
+        if ( throwable == null )
+        {
+            return ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+        Throwable throwables[] = getThrowables( throwable );
+        int count = throwables.length;
+        ArrayList frames = new ArrayList();
+        List nextTrace = getStackFrameList( throwables[count - 1] );
+        for ( int i = count; --i >= 0; )
+        {
+            List trace = nextTrace;
+            if ( i != 0 )
+            {
+                nextTrace = getStackFrameList( throwables[i - 1] );
+                removeCommonFrames( trace, nextTrace );
+            }
+            if ( i == count - 1 )
+            {
+                frames.add( throwables[i].toString() );
+            }
+            else
+            {
+                frames.add( WRAPPED_MARKER + throwables[i].toString() );
+            }
+            for ( int j = 0; j < trace.size(); j++ )
+            {
+                frames.add( trace.get( j ) );
+            }
+        }
+        return ( String[] ) frames.toArray( new String[0] );
+    }
+
+
+    /**
+     * <p>Removes common frames from the cause trace given the two stack
+     * traces.</p>
+     *
+     * @param causeFrames   stack trace of a cause throwable
+     * @param wrapperFrames stack trace of a wrapper throwable
+     * @throws IllegalArgumentException if either argument is null
+     * @since 2.0
+     */
+    public static void removeCommonFrames( List causeFrames, List wrapperFrames )
+    {
+        if ( causeFrames == null || wrapperFrames == null )
+        {
+            throw new IllegalArgumentException( "The List must not be null" );
+        }
+        int causeFrameIndex = causeFrames.size() - 1;
+        int wrapperFrameIndex = wrapperFrames.size() - 1;
+        while ( causeFrameIndex >= 0 && wrapperFrameIndex >= 0 )
+        {
+            // Remove the frame from the cause trace if it is the same
+            // as in the wrapper trace
+            String causeFrame = ( String ) causeFrames.get( causeFrameIndex );
+            String wrapperFrame = ( String ) wrapperFrames.get( wrapperFrameIndex );
+            if ( causeFrame.equals( wrapperFrame ) )
+            {
+                causeFrames.remove( causeFrameIndex );
+            }
+            causeFrameIndex--;
+            wrapperFrameIndex--;
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Gets the stack trace from a Throwable as a String.</p>
+     *
+     * @param throwable the <code>Throwable</code> to be examined
+     * @return the stack trace as generated by the exception's
+     *         <code>printStackTrace(PrintWriter)</code> method
+     */
+    public static String getStackTrace( Throwable throwable )
+    {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw, true );
+        throwable.printStackTrace( pw );
+        return sw.getBuffer().toString();
+    }
+
+
+    /**
+     * <p>A way to get the entire nested stack-trace of an throwable.</p>
+     *
+     * @param throwable the <code>Throwable</code> to be examined
+     * @return the nested stack trace, with the root cause first
+     * @since 2.0
+     */
+    public static String getFullStackTrace( Throwable throwable )
+    {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw, true );
+        Throwable[] ts = getThrowables( throwable );
+        for ( int i = 0; i < ts.length; i++ )
+        {
+            ts[i].printStackTrace( pw );
+            if ( isNestedThrowable( ts[i] ) )
+            {
+                break;
+            }
+        }
+        return sw.getBuffer().toString();
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Captures the stack trace associated with the specified
+     * <code>Throwable</code> object, decomposing it into a list of stack
+     * frames.</p>
+     *
+     * @param throwable the <code>Throwable</code> to examine, may be null
+     * @return an array of strings describing each stack frame, never null
+     */
+    public static String[] getStackFrames( Throwable throwable )
+    {
+        if ( throwable == null )
+        {
+            return ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+        return getStackFrames( getStackTrace( throwable ) );
+    }
+
+
+    /**
+     * <p>Functionality shared between the <code>getStackFrames(Throwable)</code>
+     * methods of this and the
+     */
+    static String[] getStackFrames( String stackTrace )
+    {
+        String linebreak = SystemUtils.LINE_SEPARATOR;
+        StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
+        List list = new LinkedList();
+        while ( frames.hasMoreTokens() )
+        {
+            list.add( frames.nextToken() );
+        }
+        return ( String[] ) list.toArray( new String[list.size()] );
+    }
+
+
+    /**
+     * <p>Produces a <code>List</code> of stack frames - the message is not
+     * included.</p>
+     * <p/>
+     * <p>This works in most cases - it will only fail if the exception message
+     * contains a line that starts with: <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code></p>
+     *
+     * @param t is any throwable
+     * @return List of stack frames
+     */
+    static List getStackFrameList( Throwable t )
+    {
+        String stackTrace = getStackTrace( t );
+        String linebreak = SystemUtils.LINE_SEPARATOR;
+        StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
+        List list = new LinkedList();
+        boolean traceStarted = false;
+        while ( frames.hasMoreTokens() )
+        {
+            String token = frames.nextToken();
+            // Determine if the line starts with <whitespace>at
+            int at = token.indexOf( "at" );
+            if ( at != -1 && token.substring( 0, at ).trim().length() == 0 )
+            {
+                traceStarted = true;
+                list.add( token );
+            }
+            else if ( traceStarted )
+            {
+                break;
+            }
+        }
+        return list;
+    }
+}

Added: incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/HashCodeBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/HashCodeBuilder.java?view=auto&rev=151131
==============================================================================
--- incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/HashCodeBuilder.java (added)
+++ incubator/directory/asn1/branches/rewrite/ber/src/java/org/apache/asn1/util/HashCodeBuilder.java Wed Feb  2 23:18:42 2005
@@ -0,0 +1,806 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.asn1.util;
+
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * <p>Assists in implementing {@link Object#hashCode()} methods.</p>
+ * <p/>
+ * <p> This class enables a good <code>hashCode</code> method to be built for
+ * any class. It follows the rules laid out in the book <a
+ * href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
+ * by Joshua Bloch. Writing a good <code>hashCode</code> method is actually
+ * quite difficult. This class aims to simplify the process.</p>
+ * <p/>
+ * <p>All relevant fields from the object should be included in the
+ * <code>hashCode</code> method. Derived fields may be excluded. In general, any
+ * field used in the <code>equals</code> method must be used in the
+ * <code>hashCode</code> method.</p>
+ * <p/>
+ * <p>To use this class write code as follows:</p>
+ * <pre>
+ * public class Person {
+ *   String name;
+ *   int age;
+ *   boolean isSmoker;
+ *   ...
+ * <p/>
+ *   public int hashCode() {
+ *     // you pick a hard-coded, randomly chosen, non-zero, odd number
+ *     // ideally different for each class
+ *     return new HashCodeBuilder(17, 37).
+ *       append(name).
+ *       append(age).
+ *       append(smoker).
+ *       toHashCode();
+ *   }
+ * }
+ * </pre>
+ * <p/>
+ * <p>If required, the superclass <code>hashCode()</code> can be added using
+ * {@link #appendSuper}.</p>
+ * <p/>
+ * <p>Alternatively, there is a method that uses reflection to determine the
+ * fields to test. Because these fields are usually private, the method,
+ * <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
+ * to change the visibility of the fields. This will fail under a security
+ * manager, unless the appropriate permissions are set up correctly. It is also
+ * slower than testing explicitly.</p>
+ * <p/>
+ * <p>A typical invocation for this method would look like:</p>
+ * <pre>
+ * public int hashCode() {
+ *   return HashCodeBuilder.reflectionHashCode(this);
+ * }
+ * </pre>
+ *
+ * @author Stephen Colebourne
+ * @author Gary Gregory
+ * @author Pete Gieser
+ * @version $Id: HashCodeBuilder.java,v 1.22 2004/08/15 02:17:13 bayard Exp $
+ * @since 1.0
+ */
+public class HashCodeBuilder
+{
+
+    /**
+     * Constant to use in building the hashCode.
+     */
+    private final int iConstant;
+    /**
+     * Running total of the hashCode.
+     */
+    private int iTotal = 0;
+
+
+    /**
+     * <p>Constructor.</p>
+     * <p/>
+     * <p>This constructor uses two hard coded choices for the constants needed
+     * to build a <code>hashCode</code>.</p>
+     */
+    public HashCodeBuilder()
+    {
+        super();
+
+        iConstant = 37;
+
+        iTotal = 17;
+    }
+
+
+    /**
+     * <p>Constructor.</p>
+     * <p/>
+     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
+     * these should be different for each class, however this is not vital.</p>
+     * <p/>
+     * <p>Prime numbers are preferred, especially for the multiplier.</p>
+     *
+     * @param initialNonZeroOddNumber    a non-zero, odd number used as the
+     *                                   initial value
+     * @param multiplierNonZeroOddNumber a non-zero, odd number used as the
+     *                                   multiplier
+     * @throws IllegalArgumentException if the number is zero or even
+     */
+    public HashCodeBuilder( int initialNonZeroOddNumber, int multiplierNonZeroOddNumber )
+    {
+        super();
+
+        if ( initialNonZeroOddNumber == 0 )
+        {
+            throw new IllegalArgumentException( "HashCodeBuilder requires a non zero initial value" );
+        }
+        if ( initialNonZeroOddNumber % 2 == 0 )
+        {
+            throw new IllegalArgumentException( "HashCodeBuilder requires an odd initial value" );
+        }
+        if ( multiplierNonZeroOddNumber == 0 )
+        {
+            throw new IllegalArgumentException( "HashCodeBuilder requires a non zero multiplier" );
+        }
+        if ( multiplierNonZeroOddNumber % 2 == 0 )
+        {
+            throw new IllegalArgumentException( "HashCodeBuilder requires an odd multiplier" );
+        }
+
+        iConstant = multiplierNonZeroOddNumber;
+
+        iTotal = initialNonZeroOddNumber;
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>This method uses reflection to build a valid hash code.</p>
+     * <p/>
+     * <p>This constructor uses two hard coded choices for the constants needed
+     * to build a hash code.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>Transient members will be not be used, as they are likely derived
+     * fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     *
+     * @param object the Object to create a <code>hashCode</code> for
+     * @return int hash code
+     * @throws IllegalArgumentException if the object is <code>null</code>
+     */
+    public static int reflectionHashCode( Object object )
+    {
+        return reflectionHashCode( 17, 37, object, false, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to build a valid hash code.</p>
+     * <p/>
+     * <p>This constructor uses two hard coded choices for the constants needed
+     * to build a hash code.</p>
+     * <p/>
+     * <p> It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <P>If the TestTransients parameter is set to <code>true</code>, transient
+     * members will be tested, otherwise they are ignored, as they are likely
+     * derived fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     *
+     * @param object         the Object to create a <code>hashCode</code> for
+     * @param testTransients whether to include transient fields
+     * @return int hash code
+     * @throws IllegalArgumentException if the object is <code>null</code>
+     */
+    public static int reflectionHashCode( Object object, boolean testTransients )
+    {
+        return reflectionHashCode( 17, 37, object, testTransients, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to build a valid hash code.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>Transient members will be not be used, as they are likely derived
+     * fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     * <p/>
+     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
+     * these should be different for each class, however this is not vital.
+     * Prime numbers are preferred, especially for the multiplier.</p>
+     *
+     * @param initialNonZeroOddNumber    a non-zero, odd number used as the
+     *                                   initial value
+     * @param multiplierNonZeroOddNumber a non-zero, odd number used as the
+     *                                   multiplier
+     * @param object                     the Object to create a <code>hashCode</code>
+     *                                   for
+     * @return int hash code
+     * @throws IllegalArgumentException if the Object is <code>null</code>
+     * @throws IllegalArgumentException if the number is zero or even
+     */
+    public static int reflectionHashCode( int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object )
+    {
+        return reflectionHashCode( initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to build a valid hash code.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>If the TestTransients parameter is set to <code>true</code>, transient
+     * members will be tested, otherwise they are ignored, as they are likely
+     * derived fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be tested. Superclass fields will be
+     * included.</p>
+     * <p/>
+     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
+     * these should be different for each class, however this is not vital.
+     * Prime numbers are preferred, especially for the multiplier.</p>
+     *
+     * @param initialNonZeroOddNumber    a non-zero, odd number used as the
+     *                                   initial value
+     * @param multiplierNonZeroOddNumber a non-zero, odd number used as the
+     *                                   multiplier
+     * @param object                     the Object to create a <code>hashCode</code>
+     *                                   for
+     * @param testTransients             whether to include transient fields
+     * @return int hash code
+     * @throws IllegalArgumentException if the Object is <code>null</code>
+     * @throws IllegalArgumentException if the number is zero or even
+     */
+    public static int reflectionHashCode( int initialNonZeroOddNumber, int multiplierNonZeroOddNumber,
+                                          Object object, boolean testTransients )
+    {
+        return reflectionHashCode( initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null );
+    }
+
+
+    /**
+     * <p>This method uses reflection to build a valid hash code.</p>
+     * <p/>
+     * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to
+     * private fields. This means that it will throw a security exception if run
+     * under a security manager, if the permissions are not set up correctly. It
+     * is also not as efficient as testing explicitly.</p>
+     * <p/>
+     * <p>If the TestTransients parameter is set to <code>true</code>, transient
+     * members will be tested, otherwise they are ignored, as they are likely
+     * derived fields, and not part of the value of the <code>Object</code>.</p>
+     * <p/>
+     * <p>Static fields will not be included. Superclass fields will be included
+     * up to and including the specified superclass. A null superclass is
+     * treated as java.lang.Object.</p>
+     * <p/>
+     * <p>Two randomly chosen, non-zero, odd numbers must be passed in. Ideally
+     * these should be different for each class, however this is not vital.
+     * Prime numbers are preferred, especially for the multiplier.</p>
+     *
+     * @param initialNonZeroOddNumber    a non-zero, odd number used as the
+     *                                   initial value
+     * @param multiplierNonZeroOddNumber a non-zero, odd number used as the
+     *                                   multiplier
+     * @param object                     the Object to create a <code>hashCode</code>
+     *                                   for
+     * @param testTransients             whether to include transient fields
+     * @param reflectUpToClass           the superclass to reflect up to
+     *                                   (inclusive), may be <code>null</code>
+     * @return int hash code
+     * @throws IllegalArgumentException if the Object is <code>null</code>
+     * @throws IllegalArgumentException if the number is zero or even
+     * @since 2.0
+     */
+    public static int reflectionHashCode( int initialNonZeroOddNumber,
+                                          int multiplierNonZeroOddNumber,
+                                          Object object,
+                                          boolean testTransients,
+                                          Class reflectUpToClass )
+    {
+
+        if ( object == null )
+        {
+            throw new IllegalArgumentException( "The object to build a hash code for must not be null" );
+        }
+
+        HashCodeBuilder builder = new HashCodeBuilder( initialNonZeroOddNumber, multiplierNonZeroOddNumber );
+
+        Class clazz = object.getClass();
+
+        reflectionAppend( object, clazz, builder, testTransients );
+
+        while ( clazz.getSuperclass() != null && clazz != reflectUpToClass )
+        {
+            clazz = clazz.getSuperclass();
+
+            reflectionAppend( object, clazz, builder, testTransients );
+        }
+
+        return builder.toHashCode();
+    }
+
+
+    /**
+     * <p>Appends the fields and values defined by the given object of the given
+     * <code>Class</code>.</p>
+     *
+     * @param object        the object to append details of
+     * @param clazz         the class to append details of
+     * @param builder       the builder to append to
+     * @param useTransients whether to use transient fields
+     */
+    private static void reflectionAppend( Object object, Class clazz, HashCodeBuilder builder, boolean useTransients )
+    {
+        Field[] fields = clazz.getDeclaredFields();
+
+        AccessibleObject.setAccessible( fields, true );
+
+        for ( int i = 0; i < fields.length; i++ )
+        {
+            Field f = fields[i];
+
+            if ( ( f.getName().indexOf( '$' ) == -1 )
+
+                    && ( useTransients || !Modifier.isTransient( f.getModifiers() ) )
+
+                    && ( !Modifier.isStatic( f.getModifiers() ) ) )
+            {
+                try
+                {
+                    builder.append( f.get( object ) );
+                }
+                catch ( IllegalAccessException e )
+                {
+                    //this can't happen. Would get a Security exception instead
+                    //throw a runtime exception in case the impossible happens.
+                    throw new InternalError( "Unexpected IllegalAccessException" );
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>Adds the result of super.hashCode() to this builder.</p>
+     *
+     * @param superHashCode the result of calling <code>super.hashCode()</code>
+     * @return this HashCodeBuilder, used to chain calls.
+     * @since 2.0
+     */
+    public HashCodeBuilder appendSuper( int superHashCode )
+    {
+        iTotal = iTotal * iConstant + superHashCode;
+
+        return this;
+    }
+
+    //-------------------------------------------------------------------------
+
+    /**
+     * <p>Append a <code>hashCode</code> for an <code>Object</code>.</p>
+     *
+     * @param object the Object to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( Object object )
+    {
+        if ( object == null )
+        {
+            iTotal = iTotal * iConstant;
+
+        }
+        else
+        {
+            if ( object.getClass().isArray() == false )
+            {
+                //the simple case, not an array, just the element
+                iTotal = iTotal * iConstant + object.hashCode();
+
+            }
+            else
+            {
+                //'Switch' on type of array, to dispatch to the correct handler
+                // This handles multi dimensional arrays
+                if ( object instanceof long[] )
+                {
+                    append( ( long[] ) object );
+                }
+                else if ( object instanceof int[] )
+                {
+                    append( ( int[] ) object );
+                }
+                else if ( object instanceof short[] )
+                {
+                    append( ( short[] ) object );
+                }
+                else if ( object instanceof char[] )
+                {
+                    append( ( char[] ) object );
+                }
+                else if ( object instanceof byte[] )
+                {
+                    append( ( byte[] ) object );
+                }
+                else if ( object instanceof double[] )
+                {
+                    append( ( double[] ) object );
+                }
+                else if ( object instanceof float[] )
+                {
+                    append( ( float[] ) object );
+                }
+                else if ( object instanceof boolean[] )
+                {
+                    append( ( boolean[] ) object );
+                }
+                else
+                {
+                    // Not an array of primitives
+                    append( ( Object[] ) object );
+                }
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>long</code>.</p>
+     *
+     * @param value the long to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( long value )
+    {
+        iTotal = iTotal * iConstant + ( ( int ) ( value ^ ( value >> 32 ) ) );
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for an <code>int</code>.</p>
+     *
+     * @param value the int to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( int value )
+    {
+        iTotal = iTotal * iConstant + value;
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>short</code>.</p>
+     *
+     * @param value the short to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( short value )
+    {
+        iTotal = iTotal * iConstant + value;
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>char</code>.</p>
+     *
+     * @param value the char to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( char value )
+    {
+        iTotal = iTotal * iConstant + value;
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>byte</code>.</p>
+     *
+     * @param value the byte to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( byte value )
+    {
+        iTotal = iTotal * iConstant + value;
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>double</code>.</p>
+     *
+     * @param value the double to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( double value )
+    {
+        return append( Double.doubleToLongBits( value ) );
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>float</code>.</p>
+     *
+     * @param value the float to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( float value )
+    {
+        iTotal = iTotal * iConstant + Float.floatToIntBits( value );
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>boolean</code>.</p> <p>This
+     * adds <code>iConstant * 1</code> to the <code>hashCode</code> and not a
+     * <code>1231</code> or <code>1237</code> as done in java.lang.Boolean. This
+     * is in accordance with the Effective Java design. </p>
+     *
+     * @param value the boolean to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( boolean value )
+    {
+        iTotal = iTotal * iConstant + ( value ? 0 : 1 );
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for an <code>Object</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( Object[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>long</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( long[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for an <code>int</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( int[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>short</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( short[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>char</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( char[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>byte</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( byte[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>double</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( double[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>float</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( float[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Append a <code>hashCode</code> for a <code>boolean</code> array.</p>
+     *
+     * @param array the array to add to the <code>hashCode</code>
+     * @return this
+     */
+    public HashCodeBuilder append( boolean[] array )
+    {
+        if ( array == null )
+        {
+            iTotal = iTotal * iConstant;
+        }
+        else
+        {
+            for ( int i = 0; i < array.length; i++ )
+            {
+                append( array[i] );
+            }
+        }
+
+        return this;
+    }
+
+
+    /**
+     * <p>Return the computed <code>hashCode</code>.</p>
+     *
+     * @return <code>hashCode</code> based on the fields appended
+     */
+    public int toHashCode()
+    {
+        return iTotal;
+    }
+}



Mime
View raw message