package de.axone.equals; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import de.axone.exception.Assert; public class CryptoHashCodeBuilder extends AbstractStrongHashCodeBuilder<byte[]> { private static final String ALGO = "SHA1"; private static final byte NULL_VALUE = 0; private static final int EMPTY_VALUE = Integer.MAX_VALUE-1; // "Some" rare(?) value private static final byte[] EMPTY_HASH; static{ try { EMPTY_HASH = MessageDigest.getInstance( ALGO ).digest(); } catch( NoSuchAlgorithmException e ) { throw new Error( e ); // <- this can happen } } private final MessageDigest digest; private final boolean treatEmptyAsNull; public CryptoHashCodeBuilder(){ this( true ); } public CryptoHashCodeBuilder( boolean treatEmptyAsNull ){ this.treatEmptyAsNull = treatEmptyAsNull; try { this.digest = MessageDigest.getInstance( ALGO ); } catch( NoSuchAlgorithmException e ) { throw new Error( e ); // <-- Can happen } } @Override public StrongHashCodeBuilder<byte[]> append( byte[] bytes ) { if( bytes == null ) appendNull(); else if( bytes.length == 0 ) appendEmpty(); else digest.update( bytes ); return this; } @Override public byte[] toHashCode() { return digest.digest(); } @Override public byte[] combineHash( byte[] hash1, byte[] hash2 ) { Assert.equal( hash1.length, "hash lenght", hash2.length ); byte[] result = new byte[ hash1.length ]; for( int i=0; i < hash1.length; i++ ){ result[ i ] = (byte)( ( (hash1[i]&0xff) + (hash2[i]&0xff) )&0xff ); } return result; } @Override public byte[] empty() { return EMPTY_HASH.clone(); } @Override public StrongHashCodeBuilder<byte[]> appendNull() { return append( NULL_VALUE ); } @Override public StrongHashCodeBuilder<byte[]> appendEmpty() { if( treatEmptyAsNull ) return appendNull(); else return append( EMPTY_VALUE ); } @Override StrongHashCodeBuilder<byte[]> builder() { return new CryptoHashCodeBuilder( treatEmptyAsNull ); } @Override public StrongHashCodeBuilder<byte[]> appendParent( byte[] hash ) { return append( hash ); } }