package org.okip.service.filing.impl.rfs;
import org.okip.service.filing.api.*;
/**
Copyright (c) 2002 Massachusetts Institute of Technology
This work, including software, documents, or other related items (the
"Software"), is being provided by the copyright holder(s) subject to
the terms of the MIT OKI API Implementation License. By
obtaining, using and/or copying this Software, you agree that you have
read, understand, and will comply with the following terms and
conditions of the MIT OKI API Implementation License:
Permission to use, copy, modify, and distribute this Software and its
documentation, with or without modification, for any purpose and
without fee or royalty is hereby granted, provided that you include
the following on ALL copies of the Software or portions thereof,
including modifications or derivatives, that you make:
* The full text of the MIT OKI API Implementation License in a
location viewable to users of the redistributed or derivative
work.
* Any pre-existing intellectual property disclaimers, notices, or
terms and conditions. If none exist, a short notice similar to the
following should be used within the body of any redistributed or
derivative Software:
"Copyright (c) 2002 Massachusetts Institute of Technology
All Rights Reserved."
* Notice of any changes or modifications to the MIT OKI
Software, including the date the changes were made. Any modified
software must be distributed in such as manner as to avoid any
confusion with the original MIT OKI Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The name and trademarks of copyright holder(s) and/or MIT may NOT be
used in advertising or publicity pertaining to the Software without
specific, written prior permission. Title to copyright in the Software
and any associated documentation will at all times remain with the
copyright holders.
The export of software employing encryption technology may require a
specific license from the United States Government. It is the
responsibility of any person or organization contemplating export to
obtain such a license before exporting this Software.
*/
/*
* $Source: /home/svn/cvs2svn-2.1.1/at-cvs-repo/VUE2/src/oki/old/filing/RfsInputStream.java,v $
*/
/**
* RFS InputStream. Provides a buffered OkiInputStream to a local or remote file.
* RfsInputBuffer handles the actual data transfer and read calls.
* <p>
* Is not seekable.
* </p>
* <p>
* Licensed under the {@link org.okip.service.ApiImplementationLicenseMIT MIT OKI API Implementation License}.
*
* @version $Name: not supported by cvs2svn $ / $Revision: 1.1 $ / $Date: 2003-04-14 20:48:28 $
*/
public class RfsInputStream
implements OkiInputStream
{
private String idStr = null;
private long filePos = 0; // the position in the file
private long fileStart = 0; // the position in the file of the START of the current buffer
private int bufPos = 0; // the position within the current RfsInputBuffer
private int bufferSize;
private DataBlock curBuffer;
private boolean noMoreBuffers = false;
private RfsInputBuffer inBuf;
private RfsByteStore rfsEntry;
private java.io.FileInputStream inStream;
private boolean localPassthru;
protected RfsInputStream(RfsByteStore rfsEntry)
throws FilingException
{
this.rfsEntry = rfsEntry;
this.idStr = rfsEntry.idStr;
this.bufferSize = rfsEntry.factory.getIOBufferSize();
this.localPassthru = !rfsEntry.hasClient;
if (this.idStr == null)
throw new FilingException("null filename");
}
private java.io.FileInputStream getInputStream()
throws FilingException
{
if (!localPassthru)
throw new FilingException("RfsInputStream internal error: attempt to get local stream remotely");
try {
if (this.inStream == null)
this.inStream = new java.io.FileInputStream(rfsEntry.idStr);
} catch (java.io.FileNotFoundException e) {
throw new NotFoundException(e, rfsEntry.idStr);
}
return this.inStream;
}
private void nullbuffer()
{
if (curBuffer != null) {
curBuffer.buf = null;
curBuffer = null;
}
}
private DataBlock getBuffer()
throws FilingException
{
if (curBuffer != null && bufPos < curBuffer.length)
return curBuffer;
if (noMoreBuffers) {
nullbuffer();
return null;
}
// we're going to be fetching a new buffer...
if (curBuffer != null) {
fileStart += curBuffer.length;
curBuffer.buf = null;
}
/*
* If we're being called remotely, this.in will be
* null every time. If locally, we keep re-using
* the same RfsInputBuffer.
*/
if (inBuf == null)
inBuf = new RfsInputBuffer(rfsEntry.factory, idStr, fileStart, bufferSize);
else
inBuf.setStartPosition(fileStart);
if (filePos < fileStart)
throw new FilingException("filePos<fileStart");
bufPos = (int) (filePos - fileStart);
curBuffer = inBuf.readBuffer();
if (curBuffer == null || curBuffer.containsEOF)
noMoreBuffers = true;
if (bufPos < 0 || (curBuffer != null && bufPos >= curBuffer.length))
throw new FilingException("bad bufPos="+bufPos+">="+curBuffer.length);
return curBuffer;
}
/*
* Reads up to a full IO buffer full of data and returns
* the internal array of bytes -- internal use only for
* fast copies.
*/
protected DataBlock readMax()
throws FilingException
{
if (localPassthru) {
try {
if (curBuffer == null) {
int bsize = Math.min(bufferSize, getInputStream().available());
curBuffer = new DataBlock(new byte[bsize], 0);
}
int got = getInputStream().read(curBuffer.buf);
if (got <= 0)
nullbuffer();
else
curBuffer.length = got;
} catch (java.io.IOException e) {
throw new FilingException(e);
}
} else {
if (getBuffer() == null)
return null;
if (bufPos != 0)
throw new FilingException("readMax must not follow other read calls (bp="+bufPos+")");
bufPos = curBuffer.length;
filePos += curBuffer.length;
}
return curBuffer;
}
/**
* Reads up to len bytes of data from the IO Object into an
* array of bytes.
* @param b the buffer into which the data is read.
* @param off the start offset in array <code>b</code>
* at which the data is written.
* @param max the maximum number of bytes to read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached. The amount copied on this
* call may be < max even if there's more in the file due
* to buffering implementation details.
*/
public int read(byte[] b, int off, int max)
throws FilingException
{
if (localPassthru) {
try {
return getInputStream().read(b, off, max);
} catch (java.io.IOException e) {
throw new FilingException(e);
}
}
/* For this read, we may copy out less than max even if there's
* more data available because we only send what remains in
* our current buffer. This is fine. The next call to read
* will initiate the next buffer. (So the semantics of the
* return value need to paid particular attention to: e.g.,
* we've only reached EOF if we return -1, not just because we
* may have returned less than max.
*/
if (getBuffer() == null)
return -1;
try {
int available = curBuffer.length - bufPos;
int toSend = available >= max ? max : available;
System.arraycopy(curBuffer.buf, bufPos, b, off, toSend);
bufPos += toSend;
filePos += toSend;
return toSend;
}
catch (Exception e) {
throw new FilingException(e);
}
}
/**
* Reads some number of bytes from the IO Object and stores
* them into the buffer array b.
*/
public int read(byte[] b)
throws FilingException
{
return read(b, 0, b.length);
}
/**
* Reads the next byte of data from the IO Object.
*/
public int read()
throws FilingException
{
if (localPassthru) {
try {
return getInputStream().read();
} catch (java.io.IOException e) {
throw new FilingException(e);
}
}
if (getBuffer() == null)
return -1;
// We must use & return a char here to
// avoid file data appearing as the
// value -1, which using either byte or int
// will cause.
char c = (char) curBuffer.buf[bufPos];
skip(1);
return c;
}
/**
* Skips over and discards n bytes of data from this IO Object.
* This implementation allows skipping backwards up to
* whatever is in our current buffer if we've got a
* remote client (which is autmoatically buffered).
* It will NOT allow skipping beyond EOF, unlike the
* java implementation (at least on OSX Java 1.3.1).
*/
public long skip(long n)
throws FilingException
{
if (localPassthru) {
try {
return getInputStream().skip(n);
} catch (java.io.IOException e) {
throw new FilingException(e);
}
}
if (n == 0)
return 0;
long oldPos = filePos;
long newPos;
if (oldPos + n < fileStart) {
// n is less than 0 and we're trying tt
// back up past current buffer full
newPos = fileStart;
n += (fileStart - (oldPos+n));
} else
newPos = oldPos + n;
int windowSize;
if (curBuffer == null) {
// no buffer -- we're just repositioning
fileStart = newPos;
windowSize = this.bufferSize;
} else
windowSize = curBuffer.length;
if (curBuffer == null || newPos < fileStart || newPos >= fileStart + windowSize) {
// If we've moved outside the contents of the
// current buffer, invalidate current buffer.
filePos = newPos;
long eofPos = rfsEntry.length();
if (filePos >= eofPos)
filePos = eofPos-1;
if (filePos > eofPos - windowSize) {
// We're close to end of file -- back buffer up
// against EOF
fileStart = eofPos - windowSize;
if (fileStart < 0)
fileStart = 0;
} else
fileStart = filePos;
bufPos = (int) (filePos - fileStart);
nullbuffer();
} else {
// skip within existing buffer
bufPos += n;
filePos += n;
}
getBuffer();
return (fileStart+bufPos) - oldPos;
}
/**
* Returns the number of bytes that can be read (or skipped over)
* from this IO Object without blocking by the next caller of a
* method for this IO Object.
*/
public long available()
throws FilingException
{
if (localPassthru) {
try {
return getInputStream().available();
} catch (java.io.IOException e) {
throw new FilingException(e);
}
}
if (curBuffer == null)
return 0;
else
return curBuffer.length - bufPos;
}
/**
* Closes this IO Object and releases any system resources
* associated with the IO Object.
*/
public void close()
throws FilingException
{
noMoreBuffers = false;
nullbuffer();
if (inBuf != null) {
inBuf.close();
inBuf = null;
}
if (inStream != null) {
try {
inStream.close();
inStream = null;
} catch (java.io.IOException e) {
throw new FilingException(e);
}
}
}
public java.io.InputStream getNativeInputStream()
throws FilingException {
throw new UnsupportedFilingOperationException("getNativeInputStream not supported");
}
public void mark(int readlimit)
throws FilingException {
throw new UnsupportedFilingOperationException("mark not supported");
}
public void reset()
throws FilingException {
throw new UnsupportedFilingOperationException("reset not supported");
}
public boolean markSupported() {
return false;
// this would be easy to implement
// by simply retaining a list of our
// input buffers as we get them and
// re-activating them as needed.
}
public String toString()
{
String s = "RfsInputStream[" + idStr + "] fp="+filePos + " bp="+bufPos;
if (localPassthru)
s += " LOCAL";
if (curBuffer == null)
s += " <nullbuf>";
else if (curBuffer.length <= 16)
s += " [" + new String(curBuffer.buf) + "]";
return s;
}
}