org.apache.pdfbox.io.ByteArrayPushBackInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdfbox Show documentation
Show all versions of pdfbox Show documentation
The Apache PDFBox library is an open source Java tool for working with PDF documents.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* PushBackInputStream for byte arrays.
*
* The inheritance from PushBackInputStream is only to avoid the
* introduction of an interface with all PushBackInputStream
* methods. The parent PushBackInputStream is not used in any way and
* all methods are overridden. (Thus when adding new methods to PushBackInputStream
* override them in this class as well!)
* unread() is limited to the number of bytes already read from this stream (i.e.
* the current position in the array). This limitation usually poses no problem
* to a parser, but allows for some optimization since only one array has to
* be dealt with.
*
* Note: This class is not thread safe. Clients must provide synchronization
* if needed.
*
* Note: Calling unread() after mark() will cause (part of) the unread data to be
* read again after reset(). Thus do not call unread() between mark() and reset().
*
* @author Andreas Weiss ([email protected])
* @version $Revision: 1.2 $
*/
public class ByteArrayPushBackInputStream extends PushBackInputStream
{
private byte[] data;
private int datapos;
private int datalen;
private int save;
// dummy for base class constructor
private static final InputStream DUMMY = new ByteArrayInputStream("".getBytes());
/**
* Constructor.
* @param input Data to read from. Note that calls to unread() will
* modify this array! If this is not desired, pass a copy.
*
* @throws IOException If there is an IO error.
*/
public ByteArrayPushBackInputStream(byte[] input) throws IOException
{
super(DUMMY, 1);
data = input;
datapos = 0;
save = datapos;
datalen = input != null ? input.length : 0;
}
/**
* This will peek at the next byte.
*
* @return The next byte on the stream, leaving it as available to read.
*/
public int peek()
{
try
{
// convert negative values to 128..255
return (data[datapos] + 0x100) & 0xff;
}
catch (ArrayIndexOutOfBoundsException ex)
{
// could check this before, but this is a rare case
// and this method is called sufficiently often to justify this
// optimization
return -1;
}
}
/**
* A simple test to see if we are at the end of the stream.
*
* @return true if we are at the end of the stream.
*/
public boolean isEOF()
{
return datapos >= datalen;
}
/**
* Save the state of this stream.
* @param readlimit Has no effect.
* @see InputStream#mark(int)
*/
public void mark(int readlimit)
{
if (false)
{
++readlimit; // avoid unused param warning
}
save = datapos;
}
/**
* Check if mark is supported.
* @return Always true.
* @see InputStream#markSupported()
*/
public boolean markSupported()
{
return true;
}
/**
* Restore the state of this stream to the last saveState call.
* @see InputStream#reset()
*/
public void reset()
{
datapos = save;
}
/** Available bytes.
* @see InputStream#available()
* @return Available bytes.
*/
public int available()
{
int av = datalen - datapos;
return av > 0 ? av : 0;
}
/** Totally available bytes in the underlying array.
* @return Available bytes.
*/
public int size()
{
return datalen;
}
/**
* Pushes back a byte.
* After this method returns, the next byte to be read will have the value (byte)by.
* @param by the int value whose low-order byte is to be pushed back.
* @throws IOException - If there is not enough room in the buffer for the byte.
* @see java.io.PushbackInputStream#unread(int)
*/
public void unread(int by) throws IOException
{
if (datapos == 0)
{
throw new IOException("ByteArrayParserInputStream.unread(int): " +
"cannot unread 1 byte at buffer position " + datapos);
}
--datapos;
data[datapos] = (byte)by;
}
/**
* Pushes back a portion of an array of bytes by copying it to the
* front of the pushback buffer. After this method returns, the next byte
* to be read will have the value b[off], the byte after that will have
* the value b[off+1], and so forth.
* @param buffer the byte array to push back.
* @param off the start offset of the data.
* @param len the number of bytes to push back.
* @throws IOException If there is not enough room in the pushback buffer
* for the specified number of bytes.
* @see java.io.PushbackInputStream#unread(byte[], int, int)
*/
public void unread(byte[] buffer, int off, int len) throws IOException
{
if (len <= 0 || off >= buffer.length)
{
return;
}
if (off < 0)
{
off = 0;
}
if (len > buffer.length)
{
len = buffer.length;
}
localUnread(buffer, off, len);
}
/**
* Pushes back a portion of an array of bytes by copying it to the
* front of the pushback buffer. After this method returns, the next byte
* to be read will have the value buffer[0], the byte after that will have
* the value buffer[1], and so forth.
* @param buffer the byte array to push back.
* @throws IOException If there is not enough room in the pushback buffer
* for the specified number of bytes.
* @see java.io.PushbackInputStream#unread(byte[])
*/
public void unread(byte[] buffer) throws IOException
{
localUnread(buffer, 0, buffer.length);
}
/**
* Pushes back a portion of an array of bytes by copying it to the
* front of the pushback buffer. After this method returns, the next byte
* to be read will have the value buffer[off], the byte after that will have
* the value buffer[off+1], and so forth.
* Internal method that assumes off and len to be valid.
* @param buffer the byte array to push back.
* @param off the start offset of the data.
* @param len the number of bytes to push back.
* @throws IOException If there is not enough room in the pushback buffer
* for the specified number of bytes.
* @see java.io.PushbackInputStream#unread(byte[], int, int)
*/
private void localUnread(byte[] buffer, int off, int len) throws IOException
{
if (datapos < len)
{
throw new IOException("ByteArrayParserInputStream.unread(int): " +
"cannot unread " + len +
" bytes at buffer position " + datapos);
}
datapos -= len;
System.arraycopy(buffer, off, data, datapos, len);
}
/**
* Read a byte.
* @see InputStream#read()
* @return Byte read or -1 if no more bytes are available.
*/
public int read()
{
try
{
// convert negative values to 128..255
return (data[datapos++] + 0x100) & 0xff;
}
catch (ArrayIndexOutOfBoundsException ex)
{
// could check this before, but this is a rare case
// and this method is called sufficiently often to justify this
// optimization
datapos = datalen;
return -1;
}
}
/**
* Read a number of bytes.
* @see InputStream#read(byte[])
* @param buffer the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or -1 if there
* is no more data because the end of the stream has been reached.
*/
public int read(byte[] buffer)
{
return localRead(buffer, 0, buffer.length);
}
/**
* Read a number of bytes.
* @see InputStream#read(byte[], int, int)
* @param buffer the buffer into which the data is read.
* @param off the start offset in array buffer at which the data is written.
* @param len the maximum number of bytes to read.
* @return the total number of bytes read into the buffer, or -1 if there
* is no more data because the end of the stream has been reached.
*/
public int read(byte[] buffer, int off, int len)
{
if (len <= 0 || off >= buffer.length)
{
return 0;
}
if (off < 0)
{
off = 0;
}
if (len > buffer.length)
{
len = buffer.length;
}
return localRead(buffer, off, len);
}
/**
* Read a number of bytes. Internal method that assumes off and len to be
* valid.
* @see InputStream#read(byte[], int, int)
* @param buffer the buffer into which the data is read.
* @param off the start offset in array buffer at which the data is written.
* @param len the maximum number of bytes to read.
* @return the total number of bytes read into the buffer, or -1 if there
* is no more data because the end of the stream has been reached.
*/
public int localRead(byte[] buffer, int off, int len)
{
if (len == 0)
{
return 0; // must return 0 even if at end!
}
else if (datapos >= datalen)
{
return -1;
}
else
{
int newpos = datapos + len;
if (newpos > datalen)
{
newpos = datalen;
len = newpos - datapos;
}
System.arraycopy(data, datapos, buffer, off, len);
datapos = newpos;
return len;
}
}
/**
* Skips over and discards n bytes of data from this input stream.
* The skip method may, for a variety of reasons, end up skipping over some
* smaller number of bytes, possibly 0. This may result from any of a number
* of conditions; reaching end of file before n bytes have been skipped is
* only one possibility. The actual number of bytes skipped is returned.
* If n is negative, no bytes are skipped.
* @param num the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @see InputStream#skip(long)
*/
public long skip(long num)
{
if (num <= 0)
{
return 0;
}
else
{
long newpos = datapos + num;
if (newpos >= datalen)
{
num = datalen - datapos;
datapos = datalen;
}
else
{
datapos = (int)newpos;
}
return num;
}
}
/** Position the stream at a given index. Positioning the stream
* at position size() will cause the next call to read() to return -1.
*
* @param newpos Position in the underlying array. A negative value will be
* interpreted as 0, a value greater than size() as size().
* @return old position.
*/
public int seek(int newpos)
{
if (newpos < 0)
{
newpos = 0;
}
else if (newpos > datalen)
{
newpos = datalen;
}
int oldpos = pos;
pos = newpos;
return oldpos;
}
}