001: /**
002: * Copyright (c) 2004, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.io;
031:
032: import java.io.ByteArrayInputStream;
033: import java.io.IOException;
034: import java.io.InputStream;
035:
036: /**
037: * PushBackInputStream for byte arrays.
038: *
039: * The inheritance from PushBackInputStream is only to avoid the
040: * introduction of an interface with all PushBackInputStream
041: * methods. The parent PushBackInputStream is not used in any way and
042: * all methods are overridden. (Thus when adding new methods to PushBackInputStream
043: * override them in this class as well!)
044: * unread() is limited to the number of bytes already read from this stream (i.e.
045: * the current position in the array). This limitation usually poses no problem
046: * to a parser, but allows for some optimization since only one array has to
047: * be dealt with.
048: *
049: * Note: This class is not thread safe. Clients must provide synchronization
050: * if needed.
051: *
052: * Note: Calling unread() after mark() will cause (part of) the unread data to be
053: * read again after reset(). Thus do not call unread() between mark() and reset().
054: *
055: * @author Andreas Weiss (andreas.weiss@switzerland.org)
056: * @version $Revision: 1.2 $
057: */
058: public class ByteArrayPushBackInputStream extends PushBackInputStream {
059: private byte[] data;
060: private int datapos;
061: private int datalen;
062: private int save;
063:
064: // dummy for base class constructor
065: private static final InputStream DUMMY = new ByteArrayInputStream(
066: "".getBytes());
067:
068: /**
069: * Constructor.
070: * @param input Data to read from. Note that calls to unread() will
071: * modify this array! If this is not desired, pass a copy.
072: *
073: * @throws IOException If there is an IO error.
074: */
075: public ByteArrayPushBackInputStream(byte[] input)
076: throws IOException {
077: super (DUMMY, 1);
078: data = input;
079: datapos = 0;
080: save = datapos;
081: datalen = input != null ? input.length : 0;
082: }
083:
084: /**
085: * This will peek at the next byte.
086: *
087: * @return The next byte on the stream, leaving it as available to read.
088: */
089: public int peek() {
090: try {
091: // convert negative values to 128..255
092: return (data[datapos] + 0x100) & 0xff;
093: } catch (ArrayIndexOutOfBoundsException ex) {
094: // could check this before, but this is a rare case
095: // and this method is called sufficiently often to justify this
096: // optimization
097: return -1;
098: }
099: }
100:
101: /**
102: * A simple test to see if we are at the end of the stream.
103: *
104: * @return true if we are at the end of the stream.
105: */
106: public boolean isEOF() {
107: return datapos >= datalen;
108: }
109:
110: /**
111: * Save the state of this stream.
112: * @param readlimit Has no effect.
113: * @see InputStream#mark(int)
114: */
115: public void mark(int readlimit) {
116: if (false) {
117: ++readlimit; // avoid unused param warning
118: }
119: save = datapos;
120: }
121:
122: /**
123: * Check if mark is supported.
124: * @return Always true.
125: * @see InputStream#markSupported()
126: */
127: public boolean markSupported() {
128: return true;
129: }
130:
131: /**
132: * Restore the state of this stream to the last saveState call.
133: * @see InputStream#reset()
134: */
135: public void reset() {
136: datapos = save;
137: }
138:
139: /** Available bytes.
140: * @see InputStream#available()
141: * @return Available bytes.
142: */
143: public int available() {
144: int av = datalen - datapos;
145: return av > 0 ? av : 0;
146: }
147:
148: /** Totally available bytes in the underlying array.
149: * @return Available bytes.
150: */
151: public int size() {
152: return datalen;
153: }
154:
155: /**
156: * Pushes back a byte.
157: * After this method returns, the next byte to be read will have the value (byte)by.
158: * @param by the int value whose low-order byte is to be pushed back.
159: * @throws IOException - If there is not enough room in the buffer for the byte.
160: * @see java.io.PushbackInputStream#unread(int)
161: */
162: public void unread(int by) throws IOException {
163: if (datapos == 0) {
164: throw new IOException(
165: "ByteArrayParserInputStream.unread(int): "
166: + "cannot unread 1 byte at buffer position "
167: + datapos);
168: }
169: --datapos;
170: data[datapos] = (byte) by;
171: }
172:
173: /**
174: * Pushes back a portion of an array of bytes by copying it to the
175: * front of the pushback buffer. After this method returns, the next byte
176: * to be read will have the value b[off], the byte after that will have
177: * the value b[off+1], and so forth.
178: * @param buffer the byte array to push back.
179: * @param off the start offset of the data.
180: * @param len the number of bytes to push back.
181: * @throws IOException If there is not enough room in the pushback buffer
182: * for the specified number of bytes.
183: * @see java.io.PushbackInputStream#unread(byte[], int, int)
184: */
185: public void unread(byte[] buffer, int off, int len)
186: throws IOException {
187: if (len <= 0 || off >= buffer.length) {
188: return;
189: }
190: if (off < 0) {
191: off = 0;
192: }
193: if (len > buffer.length) {
194: len = buffer.length;
195: }
196: localUnread(buffer, off, len);
197: }
198:
199: /**
200: * Pushes back a portion of an array of bytes by copying it to the
201: * front of the pushback buffer. After this method returns, the next byte
202: * to be read will have the value buffer[0], the byte after that will have
203: * the value buffer[1], and so forth.
204: * @param buffer the byte array to push back.
205: * @throws IOException If there is not enough room in the pushback buffer
206: * for the specified number of bytes.
207: * @see java.io.PushbackInputStream#unread(byte[])
208: */
209: public void unread(byte[] buffer) throws IOException {
210: localUnread(buffer, 0, buffer.length);
211: }
212:
213: /**
214: * Pushes back a portion of an array of bytes by copying it to the
215: * front of the pushback buffer. After this method returns, the next byte
216: * to be read will have the value buffer[off], the byte after that will have
217: * the value buffer[off+1], and so forth.
218: * Internal method that assumes off and len to be valid.
219: * @param buffer the byte array to push back.
220: * @param off the start offset of the data.
221: * @param len the number of bytes to push back.
222: * @throws IOException If there is not enough room in the pushback buffer
223: * for the specified number of bytes.
224: * @see java.io.PushbackInputStream#unread(byte[], int, int)
225: */
226: private void localUnread(byte[] buffer, int off, int len)
227: throws IOException {
228: if (datapos < len) {
229: throw new IOException(
230: "ByteArrayParserInputStream.unread(int): "
231: + "cannot unread " + len
232: + " bytes at buffer position " + datapos);
233: }
234: datapos -= len;
235: System.arraycopy(buffer, off, data, datapos, len);
236: }
237:
238: /**
239: * Read a byte.
240: * @see InputStream#read()
241: * @return Byte read or -1 if no more bytes are available.
242: */
243: public int read() {
244: try {
245: // convert negative values to 128..255
246: return (data[datapos++] + 0x100) & 0xff;
247: } catch (ArrayIndexOutOfBoundsException ex) {
248: // could check this before, but this is a rare case
249: // and this method is called sufficiently often to justify this
250: // optimization
251: datapos = datalen;
252: return -1;
253: }
254: }
255:
256: /**
257: * Read a number of bytes.
258: * @see InputStream#read(byte[])
259: * @param buffer the buffer into which the data is read.
260: * @return the total number of bytes read into the buffer, or -1 if there
261: * is no more data because the end of the stream has been reached.
262: */
263: public int read(byte[] buffer) {
264: return localRead(buffer, 0, buffer.length);
265: }
266:
267: /**
268: * Read a number of bytes.
269: * @see InputStream#read(byte[], int, int)
270: * @param buffer the buffer into which the data is read.
271: * @param off the start offset in array buffer at which the data is written.
272: * @param len the maximum number of bytes to read.
273: * @return the total number of bytes read into the buffer, or -1 if there
274: * is no more data because the end of the stream has been reached.
275: */
276: public int read(byte[] buffer, int off, int len) {
277: if (len <= 0 || off >= buffer.length) {
278: return 0;
279: }
280: if (off < 0) {
281: off = 0;
282: }
283: if (len > buffer.length) {
284: len = buffer.length;
285: }
286: return localRead(buffer, off, len);
287: }
288:
289: /**
290: * Read a number of bytes. Internal method that assumes off and len to be
291: * valid.
292: * @see InputStream#read(byte[], int, int)
293: * @param buffer the buffer into which the data is read.
294: * @param off the start offset in array buffer at which the data is written.
295: * @param len the maximum number of bytes to read.
296: * @return the total number of bytes read into the buffer, or -1 if there
297: * is no more data because the end of the stream has been reached.
298: */
299: public int localRead(byte[] buffer, int off, int len) {
300: if (len == 0) {
301: return 0; // must return 0 even if at end!
302: } else if (datapos >= datalen) {
303: return -1;
304: } else {
305: int newpos = datapos + len;
306: if (newpos > datalen) {
307: newpos = datalen;
308: len = newpos - datapos;
309: }
310: System.arraycopy(data, datapos, buffer, off, len);
311: datapos = newpos;
312: return len;
313: }
314: }
315:
316: /**
317: * Skips over and discards n bytes of data from this input stream.
318: * The skip method may, for a variety of reasons, end up skipping over some
319: * smaller number of bytes, possibly 0. This may result from any of a number
320: * of conditions; reaching end of file before n bytes have been skipped is
321: * only one possibility. The actual number of bytes skipped is returned.
322: * If n is negative, no bytes are skipped.
323: * @param num the number of bytes to be skipped.
324: * @return the actual number of bytes skipped.
325: * @see InputStream#skip(long)
326: */
327: public long skip(long num) {
328: if (num <= 0) {
329: return 0;
330: } else {
331: long newpos = datapos + num;
332: if (newpos >= datalen) {
333: num = datalen - datapos;
334: datapos = datalen;
335: } else {
336: datapos = (int) newpos;
337: }
338: return num;
339: }
340: }
341:
342: /** Position the stream at a given index. Positioning the stream
343: * at position size() will cause the next call to read() to return -1.
344: *
345: * @param newpos Position in the underlying array. A negative value will be
346: * interpreted as 0, a value greater than size() as size().
347: * @return old position.
348: */
349: public int seek(int newpos) {
350: if (newpos < 0) {
351: newpos = 0;
352: } else if (newpos > datalen) {
353: newpos = datalen;
354: }
355: int oldpos = pos;
356: pos = newpos;
357: return oldpos;
358: }
359:
360: }
|