//

package net.sf.zipme;
import java.io.IOException;
import java.io.InputStream;

/** 
 * InputStream that computes a checksum of the data being read using a
 * supplied Checksum object.
 * @see Checksum
 * @author Tom Tromey
 * @date May 17, 1999
 */
public class CheckedInputStream extends InputStream {
  /** 
 * This is the subordinate <code>InputStream</code> to which method calls
 * are redirected
 */
  protected InputStream in;
  /** 
 * Creates a new CheckInputStream on top of the supplied OutputStream
 * using the supplied Checksum.
 */
  public CheckedInputStream(  InputStream in,  Checksum sum){
    this.in=in;
    this.sum=sum;
  }
  /** 
 * Returns the Checksum object used. To get the data checksum computed so
 * far call <code>getChecksum.getValue()</code>.
 */
  public Checksum getChecksum(){
    return sum;
  }
  /** 
 * Reads one byte, updates the checksum and returns the read byte
 * (or -1 when the end of file was reached).
 */
  public int read() throws IOException {
    int x=in.read();
    if (x != -1)     sum.update(x);
    return x;
  }
  /** 
 * Calls the <code>read(byte[], int, int)</code> overloaded method.  
 * Note that 
 * this method does not redirect its call directly to a corresponding
 * method in <code>in</code>.  This allows subclasses to override only the
 * three argument version of <code>read</code>.
 * @param buf The buffer to read bytes into
 * @return The value retured from <code>in.read(byte[], int, int)</code>
 * @exception IOException If an error occurs
 */
  public int read(  byte[] buf) throws IOException {
    return read(buf,0,buf.length);
  }
  /** 
 * Reads at most len bytes in the supplied buffer and updates the checksum
 * with it. Returns the number of bytes actually read or -1 when the end
 * of file was reached.
 */
  public int read(  byte[] buf,  int off,  int len) throws IOException {
    int r=in.read(buf,off,len);
    if (r != -1)     sum.update(buf,off,r);
    return r;
  }
  /** 
 * Skips n bytes by reading them in a temporary buffer and updating the
 * the checksum with that buffer. Returns the actual number of bytes skiped
 * which can be less then requested when the end of file is reached.
 */
  public long skip(  long n) throws IOException {
    if (n == 0)     return 0;
    int min=(int)Math.min(n,1024);
    byte[] buf=new byte[min];
    long s=0;
    while (n > 0) {
      int r=in.read(buf,0,min);
      if (r == -1)       break;
      n-=r;
      s+=r;
      min=(int)Math.min(n,1024);
      sum.update(buf,0,r);
    }
    return s;
  }
  /** 
 * Calls the <code>in.mark(int)</code> method.
 * @param readlimit The parameter passed to <code>in.mark(int)</code>
 */
  public void mark(  int readlimit){
    in.mark(readlimit);
  }
  /** 
 * Calls the <code>in.markSupported()</code> method.
 * @return <code>true</code> if mark/reset is supported, <code>false</code>
 * otherwise
 */
  public boolean markSupported(){
    return in.markSupported();
  }
  /** 
 * Calls the <code>in.reset()</code> method.
 * @exception IOException If an error occurs
 */
  public void reset() throws IOException {
    in.reset();
  }
  /** 
 * Calls the <code>in.available()</code> method.
 * @return The value returned from <code>in.available()</code>
 * @exception IOException If an error occurs
 */
  public int available() throws IOException {
    return in.available();
  }
  /** 
 * This method closes the input stream by closing the input stream that
 * this object is filtering.  Future attempts to access this stream may
 * throw an exception.
 * @exception IOException If an error occurs
 */
  public void close() throws IOException {
    in.close();
  }
  /** 
 * The checksum object. 
 */
  private Checksum sum;
}
