/*
* $Id$
*
* Copyright (c) 2008 by Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An rereadable {@link InputStream}. The {@link #reset()} method may be
* uesd to rewind this input stream to a previously {@link #mark(int)}ed
* location.
*
* @author Joel Uckelman
* @since 3.1.0
*/
public class RereadableInputStream extends InputStream {
/*
* The implementation here is based on the one found at
* http://www.mattryall.net/blog/2007/03/composition-resettable-stream
*/
private InputStream src;
private boolean marked;
private ByteArrayOutputStream savedBytes;
/**
* Creates a <code>RereadableInputStream</code> which uses the given
* <code>InputStream</code> as its source.
*
* @param src source input stream
*/
public RereadableInputStream(InputStream src) {
if (src == null) throw new IllegalArgumentException();
this.src = src;
}
/** {@inheritDoc} */
@Override
public boolean markSupported() {
return true;
}
/**
* Set the current marked position in the stream.
*
* Note: The <code>readLimit</code> for this class determines only the
* initial size of the buffer. Reading more than <code>readLimit</code>
* bytes after calling this method will not invalidate the previous mark,
* but will cause the buffer to be resized.
*
* @param readLimit the initial buffer size
*/
@Override
public synchronized void mark(int readlimit) {
savedBytes = new ByteArrayOutputStream(readlimit);
marked = true;
}
/** {@inheritDoc} */
@Override
public synchronized void reset() throws IOException {
if (!marked)
throw new IOException("Cannot reset unmarked stream");
src = new CompositeInputStream(
new ByteArrayInputStream(savedBytes.toByteArray()), src);
marked = false;
}
/** {@inheritDoc} */
@Override
public int available() throws IOException {
return src.available();
}
/** {@inheritDoc} */
@Override
public int read() throws IOException {
final int result = src.read();
if (marked) savedBytes.write(result);
return result;
}
/** {@inheritDoc} */
@Override
public int read(byte[] b, int off, int len) throws IOException {
final int count = src.read(b, off, len);
if (count > 0 && marked) savedBytes.write(b, off, count);
return count;
}
/** {@inheritDoc} */
@Override
public void close() throws IOException {
src.close();
}
}