/*
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package sun.io;
import java.io.InputStream;
import java.io.IOException;
/**
* This class implements mark/reset functionality for InputStreams.
* Class expects Markable interface implementation from the stream.
*/
public class MarkableReader {
// Byte buffer for marked data.
private byte[] markedBytes;
// Current read position in the markedBytes.
// -1 means the stream was not resetted.
private int marker = -1;
// Size of the marked data. -1 means not marked.
private int markedLimit = -1;
// Bytes ready to be read from the buffer. Cannot be more than markedLimit.
private int markedRead = 0;
// Reference to the owner.
private Markable is;
/**
* @param is reference to Markable interface implementation
*/
public MarkableReader(Markable is) {
this.is = is;
}
/**
* Clears the marked position.
*/
private void clearPosition() {
marker = -1;
markedLimit = -1;
markedBytes = null;
markedRead = 0;
}
/**
* Reads from the stream or from the internal buffer (if the stream was resetted to a marked position) into an array of bytes.
* Blocks until some input is available.
* @param b the buffer into which the data is read
* @param off the start offset of the data
* @param len the maximum number of bytes read
* @return the actual number of bytes read, or -1 if the end of the
* entry is reached
* @exception ZipException if a ZIP file error has occurred
* @exception IOException if an I/O error has occurred
*/
public int read(byte[] b, int off, int len) throws IOException {
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
// Return value. How many bytes were actually read.
int size;
// Clear position if the end of the buffer reached
if (markedLimit == 0 || (markedRead == markedLimit && marker == markedLimit)) {
clearPosition();
}
if (markedLimit <= 0) {
// There were no marked position, just read from the stream.
size = is.readNative(b, off, len);
} else if (marker >= 0 && marker < markedRead) {
// Reading from the buffer, but not more than already read.
// The stream was resetted.
size = marker + len > markedRead ? markedRead - marker : len;
System.arraycopy(markedBytes, marker, b, off, size);
} else {
// Reading from the stream, copy into the buffer.
// marker == -1 or marker == markedRead
// The stream was marked.
size = markedRead + len > markedLimit ?
markedLimit - markedRead : len;
size = is.readNative(b, off, size);
System.arraycopy(b, off, markedBytes, markedRead, size);
markedRead += size;
}
// Move marker if read from the buffer
if (marker>=0) {
marker += size;
}
// InputStream.read() must not return 0 when called with len > 0
return (size == 0) ? -1 : size;
}
/**
* Marks the current position in the input stream. A subsequent call to
* the <code>reset</code> method repositions the stream at the last marked
* position so that subsequent reads re-read the same bytes.
*
* <p> The <code>readlimit</code> arguments tells the input stream to
* allow that many bytes to be read before the mark position gets
* invalidated.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public synchronized void mark(int readlimit) {
if (readlimit <= 0) {
return;
}
// Size to copy from the existing buffer
int size = 0;
byte[] oldBytes = markedBytes;
markedBytes = new byte[readlimit];
// If the stream was resetted and read
if (marker >= 0) {
// if requested less than available, enlarge readlimit
if (marker + readlimit < markedRead) {
readlimit = markedRead - marker;
}
size = markedRead - marker;
System.arraycopy(oldBytes, marker, markedBytes, 0, size);
marker = 0;
}
markedRead = size;
markedLimit = readlimit;
}
/**
* Repositions the stream to the position at the time the
* <code>mark</code> method was last called on the input stream.
*
* @exception IOException if this stream has not been marked or if the
* mark has been invalidated.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public synchronized void reset() throws IOException {
if (markedLimit < 0) {
throw new IOException("Position was not marked");
}
marker = 0;
}
/**
* Tests if this input stream supports the <code>mark</code> and
* <code>reset</code> methods. Whether or not <code>mark</code> and
* <code>reset</code> are supported is an invariant property of a
* particular input stream instance. The <code>markSupported</code> method
* of <code>InputStream</code> returns <code>false</code>.
*
* @return <code>true</code> if this stream instance supports the mark
* and reset methods; <code>false</code> otherwise.
* @see java.io.InputStream#markSupported()
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public boolean markSupported() {
return true;
}
}