com.aowagie.text.pdf.RandomAccessFileOrArray Maven / Gradle / Ivy
/*
* $Id: RandomAccessFileOrArray.java 3488 2008-06-02 14:19:26Z blowagie $
*
* Copyright 2001, 2002 Paulo Soares
*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the License.
*
* The Original Code is 'iText, a free JAVA-PDF library'.
*
* The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
* the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
* All Rights Reserved.
* Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
* are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
*
* Contributor(s): all the names of the contributors are added in the source code
* where applicable.
*
* Alternatively, the contents of this file may be used under the terms of the
* LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
* provisions of LGPL are applicable instead of those above. If you wish to
* allow use of your version of this file only under the terms of the LGPL
* License and not to allow others to use your version of this file under
* the MPL, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the LGPL.
* If you do not delete the provisions above, a recipient may use your version
* of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MPL as stated above or under the terms of the GNU
* Library General Public License as published by the Free Software Foundation;
* either version 2 of the License, or any later version.
*
* 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.
*
* If you didn't download this code from the following link, you should check if
* you aren't using an obsolete version:
* http://www.lowagie.com/iText/
*/
package com.aowagie.text.pdf;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.channels.FileChannel;
import com.aowagie.text.Document;
/** An implementation of a RandomAccessFile for input only
* that accepts a file or a byte array as data source.
*
* @author Paulo Soares ([email protected])
*/
public class RandomAccessFileOrArray implements DataInput {
private MappedRandomAccessFile rf;
private RandomAccessFile trf;
private boolean plainRandomAccess;
private String filename;
private byte arrayIn[];
private int arrayInPtr;
private byte back;
private boolean isBack = false;
/** Holds value of property startOffset. */
private int startOffset = 0;
public RandomAccessFileOrArray(final String filename) throws IOException {
this(filename, false, Document.plainRandomAccess);
}
RandomAccessFileOrArray(final String filename, final boolean forceRead, final boolean plainRandomAccess) throws IOException {
this.plainRandomAccess = plainRandomAccess;
final File file = new File(filename);
if (!file.canRead()) {
if (filename.startsWith("file:/") || filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("jar:")) {
final InputStream is = new URL(filename).openStream();
try {
this.arrayIn = InputStreamToArray(is);
return;
}
finally {
try {is.close();}catch(final IOException ioe){}
}
}
else {
final InputStream is = BaseFont.getResourceStream(filename);
if (is == null) {
throw new IOException(filename + " not found as file or resource.");
}
try {
this.arrayIn = InputStreamToArray(is);
return;
}
finally {
try {is.close();}catch(final IOException ioe){}
}
}
}
else if (forceRead) {
InputStream s = null;
try {
s = new FileInputStream(file);
this.arrayIn = InputStreamToArray(s);
}
finally {
try {if (s != null) {s.close();}}catch(final Exception e){}
}
return;
}
this.filename = filename;
if (plainRandomAccess) {
this.trf = new RandomAccessFile(filename, "r");
} else {
this.rf = new MappedRandomAccessFile(filename, "r");
}
}
public RandomAccessFileOrArray(final URL url) throws IOException {
final InputStream is = url.openStream();
try {
this.arrayIn = InputStreamToArray(is);
}
finally {
try {is.close();}catch(final IOException ioe){}
}
}
RandomAccessFileOrArray(final InputStream is) throws IOException {
this.arrayIn = InputStreamToArray(is);
}
private static byte[] InputStreamToArray(final InputStream is) throws IOException {
final byte b[] = new byte[8192];
final ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
final int read = is.read(b);
if (read < 1) {
break;
}
out.write(b, 0, read);
}
out.close();
return out.toByteArray();
}
public RandomAccessFileOrArray(final byte arrayIn[]) {
this.arrayIn = arrayIn;
}
RandomAccessFileOrArray(final RandomAccessFileOrArray file) {
this.filename = file.filename;
this.arrayIn = file.arrayIn;
this.startOffset = file.startOffset;
this.plainRandomAccess = file.plainRandomAccess;
}
void pushBack(final byte b) {
this.back = b;
this.isBack = true;
}
public int read() throws IOException {
if(this.isBack) {
this.isBack = false;
return this.back & 0xff;
}
if (this.arrayIn == null) {
return this.plainRandomAccess ? this.trf.read() : this.rf.read();
} else {
if (this.arrayInPtr >= this.arrayIn.length) {
return -1;
}
return this.arrayIn[this.arrayInPtr++] & 0xff;
}
}
public int read(final byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
int n = 0;
if (this.isBack) {
this.isBack = false;
if (len == 1) {
b[off] = this.back;
return 1;
}
else {
n = 1;
b[off++] = this.back;
--len;
}
}
if (this.arrayIn == null) {
return (this.plainRandomAccess ? this.trf.read(b, off, len) : this.rf.read(b, off, len)) + n;
}
else {
if (this.arrayInPtr >= this.arrayIn.length) {
return -1;
}
if (this.arrayInPtr + len > this.arrayIn.length) {
len = this.arrayIn.length - this.arrayInPtr;
}
System.arraycopy(this.arrayIn, this.arrayInPtr, b, off, len);
this.arrayInPtr += len;
return len + n;
}
}
public int read(final byte b[]) throws IOException {
return read(b, 0, b.length);
}
@Override
public void readFully(final byte b[]) throws IOException {
readFully(b, 0, b.length);
}
@Override
public void readFully(final byte b[], final int off, final int len) throws IOException {
int n = 0;
do {
final int count = read(b, off + n, len - n);
if (count < 0) {
throw new EOFException();
}
n += count;
} while (n < len);
}
public long skip(final long n) throws IOException {
return skipBytes((int)n);
}
@Override
public int skipBytes(int n) throws IOException {
if (n <= 0) {
return 0;
}
int adj = 0;
if (this.isBack) {
this.isBack = false;
if (n == 1) {
return 1;
}
else {
--n;
adj = 1;
}
}
int pos;
int len;
int newpos;
pos = getFilePointer();
len = length();
newpos = pos + n;
if (newpos > len) {
newpos = len;
}
seek(newpos);
/* return the actual number of bytes skipped */
return newpos - pos + adj;
}
public void reOpen() throws IOException {
if (this.filename != null && this.rf == null && this.trf == null) {
if (this.plainRandomAccess) {
this.trf = new RandomAccessFile(this.filename, "r");
} else {
this.rf = new MappedRandomAccessFile(this.filename, "r");
}
}
seek(0);
}
private void insureOpen() throws IOException {
if (this.filename != null && this.rf == null && this.trf == null) {
reOpen();
}
}
public boolean isOpen() {
return this.filename == null || this.rf != null || this.trf != null;
}
public void close() throws IOException {
this.isBack = false;
if (this.rf != null) {
this.rf.close();
this.rf = null;
// it's very expensive to open a memory mapped file and for the usage pattern of this class
// in iText it's faster the next re-openings to be done as a plain random access
// file
this.plainRandomAccess = true;
}
else if (this.trf != null) {
this.trf.close();
this.trf = null;
}
}
public int length() throws IOException {
if (this.arrayIn == null) {
insureOpen();
return (int)(this.plainRandomAccess ? this.trf.length() : this.rf.length()) - this.startOffset;
} else {
return this.arrayIn.length - this.startOffset;
}
}
public void seek(int pos) throws IOException {
pos += this.startOffset;
this.isBack = false;
if (this.arrayIn == null) {
insureOpen();
if (this.plainRandomAccess) {
this.trf.seek(pos);
} else {
this.rf.seek(pos);
}
} else {
this.arrayInPtr = pos;
}
}
public void seek(final long pos) throws IOException {
seek((int)pos);
}
public int getFilePointer() throws IOException {
insureOpen();
final int n = this.isBack ? 1 : 0;
if (this.arrayIn == null) {
return (int)(this.plainRandomAccess ? this.trf.getFilePointer() : this.rf.getFilePointer()) - n - this.startOffset;
} else {
return this.arrayInPtr - n - this.startOffset;
}
}
@Override
public boolean readBoolean() throws IOException {
final int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return ch != 0;
}
@Override
public byte readByte() throws IOException {
final int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return (byte)ch;
}
@Override
public int readUnsignedByte() throws IOException {
final int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return ch;
}
@Override
public short readShort() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
if ((ch1 | ch2) < 0) {
throw new EOFException();
}
return (short)((ch1 << 8) + ch2);
}
/**
* Reads a signed 16-bit number from this stream in little-endian order.
* The method reads two
* bytes from this stream, starting at the current stream pointer.
* If the two bytes read, in order, are
* b1
and b2
, where each of the two values is
* between 0
and 255
, inclusive, then the
* result is equal to:
*
* (short)((b2 << 8) | b1)
*
*
* This method blocks until the two bytes are read, the end of the
* stream is detected, or an exception is thrown.
*
* @return the next two bytes of this stream, interpreted as a signed
* 16-bit number.
* @exception EOFException if this stream reaches the end before reading
* two bytes.
* @exception IOException if an I/O error occurs.
*/
public final short readShortLE() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
if ((ch1 | ch2) < 0) {
throw new EOFException();
}
return (short)((ch2 << 8) + (ch1 << 0));
}
@Override
public int readUnsignedShort() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
if ((ch1 | ch2) < 0) {
throw new EOFException();
}
return (ch1 << 8) + ch2;
}
/**
* Reads an unsigned 16-bit number from this stream in little-endian order.
* This method reads
* two bytes from the stream, starting at the current stream pointer.
* If the bytes read, in order, are
* b1
and b2
, where
* 0 <= b1, b2 <= 255
,
* then the result is equal to:
*
* (b2 << 8) | b1
*
*
* This method blocks until the two bytes are read, the end of the
* stream is detected, or an exception is thrown.
*
* @return the next two bytes of this stream, interpreted as an
* unsigned 16-bit integer.
* @exception EOFException if this stream reaches the end before reading
* two bytes.
* @exception IOException if an I/O error occurs.
*/
public final int readUnsignedShortLE() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
if ((ch1 | ch2) < 0) {
throw new EOFException();
}
return (ch2 << 8) + (ch1 << 0);
}
@Override
public char readChar() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
if ((ch1 | ch2) < 0) {
throw new EOFException();
}
return (char)((ch1 << 8) + ch2);
}
@Override
public int readInt() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
final int ch3 = this.read();
final int ch4 = this.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4;
}
/**
* Reads a signed 32-bit integer from this stream in little-endian order.
* This method reads 4
* bytes from the stream, starting at the current stream pointer.
* If the bytes read, in order, are b1
,
* b2
, b3
, and b4
, where
* 0 <= b1, b2, b3, b4 <= 255
,
* then the result is equal to:
*
* (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
*
*
* This method blocks until the four bytes are read, the end of the
* stream is detected, or an exception is thrown.
*
* @return the next four bytes of this stream, interpreted as an
* int
.
* @exception EOFException if this stream reaches the end before reading
* four bytes.
* @exception IOException if an I/O error occurs.
*/
public final int readIntLE() throws IOException {
final int ch1 = this.read();
final int ch2 = this.read();
final int ch3 = this.read();
final int ch4 = this.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
}
/**
* Reads an unsigned 32-bit integer from this stream. This method reads 4
* bytes from the stream, starting at the current stream pointer.
* If the bytes read, in order, are b1
,
* b2
, b3
, and b4
, where
* 0 <= b1, b2, b3, b4 <= 255
,
* then the result is equal to:
*
* (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
*
*
* This method blocks until the four bytes are read, the end of the
* stream is detected, or an exception is thrown.
*
* @return the next four bytes of this stream, interpreted as a
* long
.
* @exception EOFException if this stream reaches the end before reading
* four bytes.
* @exception IOException if an I/O error occurs.
*/
public final long readUnsignedInt() throws IOException {
final long ch1 = this.read();
final long ch2 = this.read();
final long ch3 = this.read();
final long ch4 = this.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
}
public final long readUnsignedIntLE() throws IOException {
final long ch1 = this.read();
final long ch2 = this.read();
final long ch3 = this.read();
final long ch4 = this.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
}
@Override
public long readLong() throws IOException {
return ((long)readInt() << 32) + (readInt() & 0xFFFFFFFFL);
}
public final long readLongLE() throws IOException {
final int i1 = readIntLE();
final int i2 = readIntLE();
return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL);
}
@Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public final float readFloatLE() throws IOException {
return Float.intBitsToFloat(readIntLE());
}
@Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public final double readDoubleLE() throws IOException {
return Double.longBitsToDouble(readLongLE());
}
@Override
public String readLine() throws IOException {
final StringBuffer input = new StringBuffer();
int c = -1;
boolean eol = false;
while (!eol) {
switch (c = read()) {
case -1:
case '\n':
eol = true;
break;
case '\r':
eol = true;
final int cur = getFilePointer();
if (read() != '\n') {
seek(cur);
}
break;
default:
input.append((char)c);
break;
}
}
if (c == -1 && input.length() == 0) {
return null;
}
return input.toString();
}
@Override
public String readUTF() throws IOException {
return DataInputStream.readUTF(this);
}
/** Getter for property startOffset.
* @return Value of property startOffset.
*
*/
public int getStartOffset() {
return this.startOffset;
}
/** Setter for property startOffset.
* @param startOffset New value of property startOffset.
*
*/
public void setStartOffset(final int startOffset) {
this.startOffset = startOffset;
}
/**
* @since 2.0.8
* @return a buffer of nio bytes
* @throws IOException Exception with information about a IO error
*/
public java.nio.ByteBuffer getNioByteBuffer() throws IOException {
if (this.filename != null) {
FileChannel channel;
if (this.plainRandomAccess) {
channel = this.trf.getChannel();
} else {
channel = this.rf.getChannel();
}
return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
}
return java.nio.ByteBuffer.wrap(this.arrayIn);
}
}