package org.cdlib.xtf.saxonExt.pipe;
/*
* Copyright (c) 2012, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the University of California nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.io.RandomAccessFile;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
/**
* Class to provide buffered, random access to a PDF file. Useful for when we
* can't realistically fit a PDF file into memory.
*
* @author Martin Haye
*/
class BufferedRandomAccessFile extends RandomAccessFileOrArray
{
// Unbuffered base file
RandomAccessFile baseFile;
// Support for pushing back a single byte
byte prevByte;
boolean havePrevByte = false;
// Buffering
final int BUFFER_SIZE = 4096;
byte[] buffer = new byte[BUFFER_SIZE];
int startOffset = 0;
int bufferLength = 0;
int bufferPos = 0;
int bufferFilePointer = 0;
String filename;
/* Constructor - open up the file and initialize the file pointer to zero */
public BufferedRandomAccessFile(String filename) throws IOException {
super(filename, false, true);
this.filename = filename;
baseFile = new RandomAccessFile(filename, "r");
}
@Override
public void pushBack(byte b) {
prevByte = b;
havePrevByte = true;
}
/**
* Fill our buffer with data at the current file pointer.
*/
private void fillBuffer() throws IOException
{
bufferFilePointer = (int) baseFile.getFilePointer();
bufferLength = baseFile.read(buffer);
bufferPos = 0;
}
@Override
public int read() throws IOException
{
if (havePrevByte) {
havePrevByte = false;
return prevByte & 0xff;
}
if (bufferPos >= bufferLength) {
fillBuffer();
if (bufferPos >= bufferLength)
return -1;
}
return buffer[bufferPos++] & 0xff;
}
@Override
public int read(byte[] b, int off, int len) throws IOException
{
int origLen = len;
// If there was a pushback, use it.
if (havePrevByte && len > 0) {
havePrevByte = false;
b[off] = prevByte;
++off;
--len;
}
// Copy as much as we can from the buffer.
int toCopy = Math.min(bufferLength - bufferPos, len);
if (toCopy > 0) {
System.arraycopy(buffer, bufferPos, b, off, toCopy);
bufferPos += toCopy;
off += toCopy;
len -= toCopy;
}
// For anything remaining, get it straight from the file.
if (len > 0) {
int nRead = baseFile.read(b, off, len);
off += nRead;
len -= nRead;
}
// And let the caller know how much we were able to read.
return origLen - len;
}
@Override
public int skipBytes(int n) throws IOException
{
int origN = n;
// Eat the 'back' byte
if (havePrevByte && n > 0) {
havePrevByte = false;
--n;
}
// Skip in the buffer if we can
int nBufSkip = Math.min(bufferLength - bufferPos, n);
if (nBufSkip > 0) {
bufferPos += nBufSkip;
n -= nBufSkip;
}
// Skip by seeking if we must
if (n > 0) {
baseFile.seek(baseFile.getFilePointer() + n);
n = 0;
}
return Math.max(0, origN - n);
}
@Override
public void reOpen() throws IOException {
if (filename != null && baseFile == null)
baseFile = new RandomAccessFile(filename, "r");
seek(0);
}
@Override
protected void insureOpen() throws IOException {
if (filename != null && baseFile == null) {
reOpen();
}
}
@Override
public boolean isOpen() {
return (baseFile != null);
}
@Override
public void close() throws IOException {
havePrevByte = false;
if (baseFile != null) {
baseFile.close();
baseFile = null;
}
super.close();
}
@Override
public void setStartOffset(int off) {
startOffset = off;
}
@Override
public int getStartOffset() {
return startOffset;
}
@Override
public int length() throws IOException {
insureOpen();
return (int) baseFile.length() - startOffset;
}
@Override
public void seek(int pos) throws IOException
{
insureOpen();
havePrevByte = false;
if (pos >= bufferFilePointer && (pos - bufferFilePointer) < bufferLength) {
bufferPos = pos - bufferFilePointer;
return;
}
baseFile.seek(pos + startOffset);
bufferLength = bufferPos = 0;
bufferFilePointer = pos;
}
@Override
public void seek(long pos) throws IOException {
seek((int)pos);
}
@Override
public int getFilePointer() throws IOException {
insureOpen();
return bufferFilePointer + bufferPos - startOffset - (havePrevByte ? 1 : 0);
}
@Override
public java.nio.ByteBuffer getNioByteBuffer() throws IOException {
throw new RuntimeException("Not supported");
}
}