package com.yoursway.utils.io; import static java.lang.Math.min; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import com.yoursway.utils.YsDigest; public class HashingInputStreamDecorator extends FilterInputStream { private final MessageDigest algorithm; private boolean eofReached = false; private final StreamCloseBehavior closeBehavior; public static HashingInputStreamDecorator md5HashingDecorator(InputStream in, StreamCloseBehavior closeBehavior) { return new HashingInputStreamDecorator(in, closeBehavior, YsDigest.createMd5()); } public static HashingInputStreamDecorator sha1HashingDecorator(InputStream in, StreamCloseBehavior closeBehavior) { return new HashingInputStreamDecorator(in, closeBehavior, YsDigest.createSha1()); } public HashingInputStreamDecorator(InputStream in, StreamCloseBehavior closeBehavior, MessageDigest algorithm) { super(in); if (algorithm == null) throw new NullPointerException("algorithm is null"); if (closeBehavior == null) throw new NullPointerException("closeBehavior is null"); this.algorithm = algorithm; this.closeBehavior = closeBehavior; } @Override public synchronized void mark(int readlimit) { throw new UnsupportedOperationException(); } @Override public boolean markSupported() { return false; } @Override public synchronized void reset() throws IOException { throw new UnsupportedOperationException(); } @Override public int read() throws IOException { int result = super.read(); if (result >= 0) algorithm.update((byte) result); else eofReached = true; return result; } @Override public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); if (result > 0) algorithm.update(b, off, result); else eofReached = true; return result; } @Override public long skip(long n) throws IOException { byte[] dummy = new byte[102400]; long result = 0; while (n > 0) { int portion = (n > Integer.MAX_VALUE ? dummy.length : min((int) n, dummy.length)); int len = read(dummy, 0, portion); if (len <= 0) break; n -= len; result += len; } return result; } @Override public void close() throws IOException { closeBehavior.close(in); } public boolean isEofReached() { return eofReached; } public byte[] getHash() { if (!eofReached) throw new IllegalStateException("Cannot obtain the hash until EOF has been reached."); return algorithm.digest(); } public String getHexHash() { return YsDigest.asHex(getHash()); } }