001: /*
002: * $Id: RandomAccessFileOrArray.java 2897 2007-08-28 11:19:45Z psoares33 $
003: * $Name$
004: *
005: * Copyright 2001, 2002 Paulo Soares
006: *
007: * The contents of this file are subject to the Mozilla Public License Version 1.1
008: * (the "License"); you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the License.
014: *
015: * The Original Code is 'iText, a free JAVA-PDF library'.
016: *
017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
019: * All Rights Reserved.
020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
022: *
023: * Contributor(s): all the names of the contributors are added in the source code
024: * where applicable.
025: *
026: * Alternatively, the contents of this file may be used under the terms of the
027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
028: * provisions of LGPL are applicable instead of those above. If you wish to
029: * allow use of your version of this file only under the terms of the LGPL
030: * License and not to allow others to use your version of this file under
031: * the MPL, indicate your decision by deleting the provisions above and
032: * replace them with the notice and other provisions required by the LGPL.
033: * If you do not delete the provisions above, a recipient may use your version
034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
035: *
036: * This library is free software; you can redistribute it and/or modify it
037: * under the terms of the MPL as stated above or under the terms of the GNU
038: * Library General Public License as published by the Free Software Foundation;
039: * either version 2 of the License, or any later version.
040: *
041: * This library is distributed in the hope that it will be useful, but WITHOUT
042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
044: * details.
045: *
046: * If you didn't download this code from the following link, you should check if
047: * you aren't using an obsolete version:
048: * http://www.lowagie.com/iText/
049: */
050:
051: package com.lowagie.text.pdf;
052:
053: import com.lowagie.text.Document;
054: import java.io.ByteArrayOutputStream;
055: import java.io.DataInput;
056: import java.io.DataInputStream;
057: import java.io.EOFException;
058: import java.io.File;
059: import java.io.FileInputStream;
060: import java.io.IOException;
061: import java.io.InputStream;
062: import java.io.RandomAccessFile;
063: import java.net.URL;
064:
065: /** An implementation of a RandomAccessFile for input only
066: * that accepts a file or a byte array as data source.
067: *
068: * @author Paulo Soares (psoares@consiste.pt)
069: */
070: public class RandomAccessFileOrArray implements DataInput {
071:
072: MappedRandomAccessFile rf;
073: RandomAccessFile trf;
074: boolean plainRandomAccess;
075: String filename;
076: byte arrayIn[];
077: int arrayInPtr;
078: byte back;
079: boolean isBack = false;
080:
081: /** Holds value of property startOffset. */
082: private int startOffset = 0;
083:
084: public RandomAccessFileOrArray(String filename) throws IOException {
085: this (filename, false, Document.plainRandomAccess);
086: }
087:
088: public RandomAccessFileOrArray(String filename, boolean forceRead,
089: boolean plainRandomAccess) throws IOException {
090: this .plainRandomAccess = plainRandomAccess;
091: File file = new File(filename);
092: if (!file.canRead()) {
093: if (filename.startsWith("file:/")
094: || filename.startsWith("http://")
095: || filename.startsWith("https://")
096: || filename.startsWith("jar:")) {
097: InputStream is = new URL(filename).openStream();
098: try {
099: this .arrayIn = InputStreamToArray(is);
100: return;
101: } finally {
102: try {
103: is.close();
104: } catch (IOException ioe) {
105: }
106: }
107: } else {
108: InputStream is = BaseFont.getResourceStream(filename);
109: if (is == null)
110: throw new IOException(filename
111: + " not found as file or resource.");
112: try {
113: this .arrayIn = InputStreamToArray(is);
114: return;
115: } finally {
116: try {
117: is.close();
118: } catch (IOException ioe) {
119: }
120: }
121: }
122: } else if (forceRead) {
123: InputStream s = null;
124: try {
125: s = new FileInputStream(file);
126: this .arrayIn = InputStreamToArray(s);
127: } finally {
128: try {
129: if (s != null) {
130: s.close();
131: }
132: } catch (Exception e) {
133: }
134: }
135: return;
136: }
137: this .filename = filename;
138: if (plainRandomAccess)
139: trf = new RandomAccessFile(filename, "r");
140: else
141: rf = new MappedRandomAccessFile(filename, "r");
142: }
143:
144: public RandomAccessFileOrArray(URL url) throws IOException {
145: InputStream is = url.openStream();
146: try {
147: this .arrayIn = InputStreamToArray(is);
148: } finally {
149: try {
150: is.close();
151: } catch (IOException ioe) {
152: }
153: }
154: }
155:
156: public RandomAccessFileOrArray(InputStream is) throws IOException {
157: this .arrayIn = InputStreamToArray(is);
158: }
159:
160: public static byte[] InputStreamToArray(InputStream is)
161: throws IOException {
162: byte b[] = new byte[8192];
163: ByteArrayOutputStream out = new ByteArrayOutputStream();
164: while (true) {
165: int read = is.read(b);
166: if (read < 1)
167: break;
168: out.write(b, 0, read);
169: }
170: return out.toByteArray();
171: }
172:
173: public RandomAccessFileOrArray(byte arrayIn[]) {
174: this .arrayIn = arrayIn;
175: }
176:
177: public RandomAccessFileOrArray(RandomAccessFileOrArray file) {
178: filename = file.filename;
179: arrayIn = file.arrayIn;
180: startOffset = file.startOffset;
181: plainRandomAccess = file.plainRandomAccess;
182: }
183:
184: public void pushBack(byte b) {
185: back = b;
186: isBack = true;
187: }
188:
189: public int read() throws IOException {
190: if (isBack) {
191: isBack = false;
192: return back & 0xff;
193: }
194: if (arrayIn == null)
195: return plainRandomAccess ? trf.read() : rf.read();
196: else {
197: if (arrayInPtr >= arrayIn.length)
198: return -1;
199: return arrayIn[arrayInPtr++] & 0xff;
200: }
201: }
202:
203: public int read(byte[] b, int off, int len) throws IOException {
204: if (len == 0)
205: return 0;
206: int n = 0;
207: if (isBack) {
208: isBack = false;
209: if (len == 1) {
210: b[off] = back;
211: return 1;
212: } else {
213: n = 1;
214: b[off++] = back;
215: --len;
216: }
217: }
218: if (arrayIn == null) {
219: return (plainRandomAccess ? trf.read(b, off, len) : rf
220: .read(b, off, len))
221: + n;
222: } else {
223: if (arrayInPtr >= arrayIn.length)
224: return -1;
225: if (arrayInPtr + len > arrayIn.length)
226: len = arrayIn.length - arrayInPtr;
227: System.arraycopy(arrayIn, arrayInPtr, b, off, len);
228: arrayInPtr += len;
229: return len + n;
230: }
231: }
232:
233: public int read(byte b[]) throws IOException {
234: return read(b, 0, b.length);
235: }
236:
237: public void readFully(byte b[]) throws IOException {
238: readFully(b, 0, b.length);
239: }
240:
241: public void readFully(byte b[], int off, int len)
242: throws IOException {
243: int n = 0;
244: do {
245: int count = read(b, off + n, len - n);
246: if (count < 0)
247: throw new EOFException();
248: n += count;
249: } while (n < len);
250: }
251:
252: public long skip(long n) throws IOException {
253: return skipBytes((int) n);
254: }
255:
256: public int skipBytes(int n) throws IOException {
257: if (n <= 0) {
258: return 0;
259: }
260: int adj = 0;
261: if (isBack) {
262: isBack = false;
263: if (n == 1) {
264: return 1;
265: } else {
266: --n;
267: adj = 1;
268: }
269: }
270: int pos;
271: int len;
272: int newpos;
273:
274: pos = getFilePointer();
275: len = length();
276: newpos = pos + n;
277: if (newpos > len) {
278: newpos = len;
279: }
280: seek(newpos);
281:
282: /* return the actual number of bytes skipped */
283: return newpos - pos + adj;
284: }
285:
286: public void reOpen() throws IOException {
287: if (filename != null && rf == null && trf == null) {
288: if (plainRandomAccess)
289: trf = new RandomAccessFile(filename, "r");
290: else
291: rf = new MappedRandomAccessFile(filename, "r");
292: }
293: seek(0);
294: }
295:
296: protected void insureOpen() throws IOException {
297: if (filename != null && rf == null && trf == null) {
298: reOpen();
299: }
300: }
301:
302: public boolean isOpen() {
303: return (filename == null || rf != null || trf != null);
304: }
305:
306: public void close() throws IOException {
307: isBack = false;
308: if (rf != null) {
309: rf.close();
310: rf = null;
311: // it's very expensive to open a memory mapped file and for the usage pattern of this class
312: // in iText it's faster the next re-openings to be done as a plain random access
313: // file
314: plainRandomAccess = true;
315: } else if (trf != null) {
316: trf.close();
317: trf = null;
318: }
319: }
320:
321: public int length() throws IOException {
322: if (arrayIn == null) {
323: insureOpen();
324: return (int) (plainRandomAccess ? trf.length() : rf
325: .length())
326: - startOffset;
327: } else
328: return arrayIn.length - startOffset;
329: }
330:
331: public void seek(int pos) throws IOException {
332: pos += startOffset;
333: isBack = false;
334: if (arrayIn == null) {
335: insureOpen();
336: if (plainRandomAccess)
337: trf.seek(pos);
338: else
339: rf.seek(pos);
340: } else
341: arrayInPtr = pos;
342: }
343:
344: public void seek(long pos) throws IOException {
345: seek((int) pos);
346: }
347:
348: public int getFilePointer() throws IOException {
349: insureOpen();
350: int n = isBack ? 1 : 0;
351: if (arrayIn == null) {
352: return (int) (plainRandomAccess ? trf.getFilePointer() : rf
353: .getFilePointer())
354: - n - startOffset;
355: } else
356: return arrayInPtr - n - startOffset;
357: }
358:
359: public boolean readBoolean() throws IOException {
360: int ch = this .read();
361: if (ch < 0)
362: throw new EOFException();
363: return (ch != 0);
364: }
365:
366: public byte readByte() throws IOException {
367: int ch = this .read();
368: if (ch < 0)
369: throw new EOFException();
370: return (byte) (ch);
371: }
372:
373: public int readUnsignedByte() throws IOException {
374: int ch = this .read();
375: if (ch < 0)
376: throw new EOFException();
377: return ch;
378: }
379:
380: public short readShort() throws IOException {
381: int ch1 = this .read();
382: int ch2 = this .read();
383: if ((ch1 | ch2) < 0)
384: throw new EOFException();
385: return (short) ((ch1 << 8) + ch2);
386: }
387:
388: /**
389: * Reads a signed 16-bit number from this stream in little-endian order.
390: * The method reads two
391: * bytes from this stream, starting at the current stream pointer.
392: * If the two bytes read, in order, are
393: * <code>b1</code> and <code>b2</code>, where each of the two values is
394: * between <code>0</code> and <code>255</code>, inclusive, then the
395: * result is equal to:
396: * <blockquote><pre>
397: * (short)((b2 << 8) | b1)
398: * </pre></blockquote>
399: * <p>
400: * This method blocks until the two bytes are read, the end of the
401: * stream is detected, or an exception is thrown.
402: *
403: * @return the next two bytes of this stream, interpreted as a signed
404: * 16-bit number.
405: * @exception EOFException if this stream reaches the end before reading
406: * two bytes.
407: * @exception IOException if an I/O error occurs.
408: */
409: public final short readShortLE() throws IOException {
410: int ch1 = this .read();
411: int ch2 = this .read();
412: if ((ch1 | ch2) < 0)
413: throw new EOFException();
414: return (short) ((ch2 << 8) + (ch1 << 0));
415: }
416:
417: public int readUnsignedShort() throws IOException {
418: int ch1 = this .read();
419: int ch2 = this .read();
420: if ((ch1 | ch2) < 0)
421: throw new EOFException();
422: return (ch1 << 8) + ch2;
423: }
424:
425: /**
426: * Reads an unsigned 16-bit number from this stream in little-endian order.
427: * This method reads
428: * two bytes from the stream, starting at the current stream pointer.
429: * If the bytes read, in order, are
430: * <code>b1</code> and <code>b2</code>, where
431: * <code>0 <= b1, b2 <= 255</code>,
432: * then the result is equal to:
433: * <blockquote><pre>
434: * (b2 << 8) | b1
435: * </pre></blockquote>
436: * <p>
437: * This method blocks until the two bytes are read, the end of the
438: * stream is detected, or an exception is thrown.
439: *
440: * @return the next two bytes of this stream, interpreted as an
441: * unsigned 16-bit integer.
442: * @exception EOFException if this stream reaches the end before reading
443: * two bytes.
444: * @exception IOException if an I/O error occurs.
445: */
446: public final int readUnsignedShortLE() throws IOException {
447: int ch1 = this .read();
448: int ch2 = this .read();
449: if ((ch1 | ch2) < 0)
450: throw new EOFException();
451: return (ch2 << 8) + (ch1 << 0);
452: }
453:
454: public char readChar() throws IOException {
455: int ch1 = this .read();
456: int ch2 = this .read();
457: if ((ch1 | ch2) < 0)
458: throw new EOFException();
459: return (char) ((ch1 << 8) + ch2);
460: }
461:
462: /**
463: * Reads a Unicode character from this stream in little-endian order.
464: * This method reads two
465: * bytes from the stream, starting at the current stream pointer.
466: * If the bytes read, in order, are
467: * <code>b1</code> and <code>b2</code>, where
468: * <code>0 <= b1, b2 <= 255</code>,
469: * then the result is equal to:
470: * <blockquote><pre>
471: * (char)((b2 << 8) | b1)
472: * </pre></blockquote>
473: * <p>
474: * This method blocks until the two bytes are read, the end of the
475: * stream is detected, or an exception is thrown.
476: *
477: * @return the next two bytes of this stream as a Unicode character.
478: * @exception EOFException if this stream reaches the end before reading
479: * two bytes.
480: * @exception IOException if an I/O error occurs.
481: */
482: public final char readCharLE() throws IOException {
483: int ch1 = this .read();
484: int ch2 = this .read();
485: if ((ch1 | ch2) < 0)
486: throw new EOFException();
487: return (char) ((ch2 << 8) + (ch1 << 0));
488: }
489:
490: public int readInt() throws IOException {
491: int ch1 = this .read();
492: int ch2 = this .read();
493: int ch3 = this .read();
494: int ch4 = this .read();
495: if ((ch1 | ch2 | ch3 | ch4) < 0)
496: throw new EOFException();
497: return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
498: }
499:
500: /**
501: * Reads a signed 32-bit integer from this stream in little-endian order.
502: * This method reads 4
503: * bytes from the stream, starting at the current stream pointer.
504: * If the bytes read, in order, are <code>b1</code>,
505: * <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
506: * <code>0 <= b1, b2, b3, b4 <= 255</code>,
507: * then the result is equal to:
508: * <blockquote><pre>
509: * (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
510: * </pre></blockquote>
511: * <p>
512: * This method blocks until the four bytes are read, the end of the
513: * stream is detected, or an exception is thrown.
514: *
515: * @return the next four bytes of this stream, interpreted as an
516: * <code>int</code>.
517: * @exception EOFException if this stream reaches the end before reading
518: * four bytes.
519: * @exception IOException if an I/O error occurs.
520: */
521: public final int readIntLE() throws IOException {
522: int ch1 = this .read();
523: int ch2 = this .read();
524: int ch3 = this .read();
525: int ch4 = this .read();
526: if ((ch1 | ch2 | ch3 | ch4) < 0)
527: throw new EOFException();
528: return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
529: }
530:
531: /**
532: * Reads an unsigned 32-bit integer from this stream. This method reads 4
533: * bytes from the stream, starting at the current stream pointer.
534: * If the bytes read, in order, are <code>b1</code>,
535: * <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
536: * <code>0 <= b1, b2, b3, b4 <= 255</code>,
537: * then the result is equal to:
538: * <blockquote><pre>
539: * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
540: * </pre></blockquote>
541: * <p>
542: * This method blocks until the four bytes are read, the end of the
543: * stream is detected, or an exception is thrown.
544: *
545: * @return the next four bytes of this stream, interpreted as a
546: * <code>long</code>.
547: * @exception EOFException if this stream reaches the end before reading
548: * four bytes.
549: * @exception IOException if an I/O error occurs.
550: */
551: public final long readUnsignedInt() throws IOException {
552: long ch1 = this .read();
553: long ch2 = this .read();
554: long ch3 = this .read();
555: long ch4 = this .read();
556: if ((ch1 | ch2 | ch3 | ch4) < 0)
557: throw new EOFException();
558: return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
559: }
560:
561: public final long readUnsignedIntLE() throws IOException {
562: long ch1 = this .read();
563: long ch2 = this .read();
564: long ch3 = this .read();
565: long ch4 = this .read();
566: if ((ch1 | ch2 | ch3 | ch4) < 0)
567: throw new EOFException();
568: return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
569: }
570:
571: public long readLong() throws IOException {
572: return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
573: }
574:
575: public final long readLongLE() throws IOException {
576: int i1 = readIntLE();
577: int i2 = readIntLE();
578: return ((long) i2 << 32) + (i1 & 0xFFFFFFFFL);
579: }
580:
581: public float readFloat() throws IOException {
582: return Float.intBitsToFloat(readInt());
583: }
584:
585: public final float readFloatLE() throws IOException {
586: return Float.intBitsToFloat(readIntLE());
587: }
588:
589: public double readDouble() throws IOException {
590: return Double.longBitsToDouble(readLong());
591: }
592:
593: public final double readDoubleLE() throws IOException {
594: return Double.longBitsToDouble(readLongLE());
595: }
596:
597: public String readLine() throws IOException {
598: StringBuffer input = new StringBuffer();
599: int c = -1;
600: boolean eol = false;
601:
602: while (!eol) {
603: switch (c = read()) {
604: case -1:
605: case '\n':
606: eol = true;
607: break;
608: case '\r':
609: eol = true;
610: int cur = getFilePointer();
611: if ((read()) != '\n') {
612: seek(cur);
613: }
614: break;
615: default:
616: input.append((char) c);
617: break;
618: }
619: }
620:
621: if ((c == -1) && (input.length() == 0)) {
622: return null;
623: }
624: return input.toString();
625: }
626:
627: public String readUTF() throws IOException {
628: return DataInputStream.readUTF(this );
629: }
630:
631: /** Getter for property startOffset.
632: * @return Value of property startOffset.
633: *
634: */
635: public int getStartOffset() {
636: return this .startOffset;
637: }
638:
639: /** Setter for property startOffset.
640: * @param startOffset New value of property startOffset.
641: *
642: */
643: public void setStartOffset(int startOffset) {
644: this.startOffset = startOffset;
645: }
646:
647: }
|