StreamPump.java

/***************************************************************************
   Copyright 2008 Emily Estes

   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 net.metanotion.io;


import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import net.metanotion.util.Pump;

/** Create a "pump" that will transfer bytes from an {@link java.io.InputStream}
to an {@link java.io.OutputStream}. The methods of this object will copy until various
"blocking" or end of stream behaviors occur. Note: at present this class does not
make use of any of the NIO classes. That is an obvious improvement to make, but it
has not been a huge priority at the moment. */
public final class StreamPump implements Pump<IOException> {
	/** The default size of the buffer to read into before writing. */
	private static final int defaultBlockSize = 2048;

	/** The input stream to transfer bytes from. */
	private final InputStream in;
	/** The output stream to transfer bytes to. */
	private final OutputStream out;
	/** The maximum block size to use when transfering bytes. */
	private final int blockSize;

	/** Create a stream pump with the default block size (2048 bytes).
		@param in The input stream to transfer bytes from.
		@param out The output stream to transfer bytes to.
	*/
	public StreamPump(final InputStream in, final OutputStream out) { this(in, out, defaultBlockSize); }

	/** Create a stream pump with a custom block size.
		@param in The input stream to transfer bytes from.
		@param out The output stream to transfer bytes to.
		@param blockSize The size of the transfer buffer.
	*/
	public StreamPump(final InputStream in, final OutputStream out, final int blockSize) {
		this.in = in;
		this.out = out;
		this.blockSize = blockSize;
	}

	/** Attempt to transfer every byte from the reader to the writer.
		This method blocks until either {@link java.io.InputStream#read(byte[])} returns 0 bytes read
		or an {@link java.io.EOFException} occurs. If either stream throws an {@link java.io.IOException}
		the transfer is interrupted and the exeption is propagated to the caller.
		@throws java.io.IOException if either in or out throws one.
	*/
	@Override public void pumpAll() throws IOException {
		final byte[] buffer = new byte[blockSize];
		try {
			while(true) {
				final int len = in.read(buffer);
				if(len < 0) { break; }
				out.write(buffer, 0, len);
			}
		} catch(EOFException eof) { }
	}

	/** Transfer bytes from in to out as long as {@link java.io.InputStream#available} > 0 is true.
		or an {@link java.io.EOFException} occurs. If either stream throws an {@link java.io.IOException}
		the transfer is interrupted and the exeption is propagated to the caller.
		@throws java.io.IOException if either in or out throws one.
	*/
	@Override public void pumpUntilBlocked() throws IOException {
		final byte[] buffer = new byte[blockSize];
		int len = 0;
		try {
			while(in.available() > 0) {
				if((len = in.read(buffer)) < 0) { break; }
				//if(len < 0) { break; }
				out.write(buffer, 0, len);
			}
		} catch(EOFException eof) { }
		if (len == -1) { throw new EOFException(); }
	}

	/** Transfer n bytes from in to out as long unless an {@link java.io.EOFException} occurs or
		{@link java.io.InputStream#read(byte[])} returns 0 bytes read. If either stream throws an
		{@link java.io.IOException} the transfer is interrupted and the exeption is propagated to the caller.
		@param n The number of bytes to transfer.
		@return The actual number of bytes transferred. Will be less than n if
			{@link java.io.InputStream#read(byte[])} returns 0 before n bytes are read.
		@throws java.io.IOException if either in or out throws one.
	*/
	@Override public int pumpCount(final int n) throws IOException {
		final byte[] buffer = new byte[blockSize];
		int read = 0;
		try {
			while(read < n) {
				int len;
				if((n - read) < blockSize) {
					len = in.read(buffer, 0, n - read);
				} else {
					len = in.read(buffer);
				}
				out.write(buffer, 0, len);
				read += len;
			}
		} catch(EOFException eof) { }
		return read;
	}

	/** Transfer n bytes from in to out as long unless an {@link java.io.EOFException} occurs or
		{@link java.io.InputStream#read(byte[])} returns 0 characters read OR {@link java.io.InputStream#available} > 0 is
		false. If either stream throws an {@link java.io.IOException} the transfer is interrupted and the
		exeption is propagated to the caller.
		@param n The number of bytes to transfer.
		@return The actual number of bytes transferred. Will be less than n if
			{@link java.io.InputStream#read(byte[])} returns 0 before n bytes are read or
			{@link java.io.InputStream#available} > 0 is false before n bytes are read, or an
			{@link java.io.EOFException} occurs.
		@throws java.io.IOException if either in or out throws one.
	*/
	@Override public int pumpCountUntilBlocked(final int n) throws IOException {
		final byte[] buffer = new byte[blockSize];
		int read = 0;
		try {
			while((read < n) && (in.available() > 0)) {
				int len;
				if((n - read) < blockSize) {
					len = in.read(buffer, 0, n - read);
				} else {
					len = in.read(buffer);
				}
				out.write(buffer, 0, len);
				read += len;
			}
		} catch(EOFException eof) { }
		return read;
	}
}