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 [EMAIL PROTECTED] 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:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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>" at".</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 [EMAIL PROTECTED] 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 + * [EMAIL PROTECTED] #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; + } +}
