001 /*
002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.imageio.stream;
027
028 import java.io.DataInput;
029 import java.io.DataInputStream;
030 import java.io.EOFException;
031 import java.io.File;
032 import java.io.FileNotFoundException;
033 import java.io.IOException;
034 import java.io.RandomAccessFile;
035 import java.nio.ByteOrder;
036 import java.util.Stack;
037 import javax.imageio.IIOException;
038
039 /**
040 * An abstract class implementing the <code>ImageInputStream</code> interface.
041 * This class is designed to reduce the number of methods that must
042 * be implemented by subclasses.
043 *
044 * <p> In particular, this class handles most or all of the details of
045 * byte order interpretation, buffering, mark/reset, discarding,
046 * closing, and disposing.
047 */
048 public abstract class ImageInputStreamImpl implements ImageInputStream {
049
050 private Stack markByteStack = new Stack();
051
052 private Stack markBitStack = new Stack();
053
054 private boolean isClosed = false;
055
056 // Length of the buffer used for readFully(type[], int, int)
057 private static final int BYTE_BUF_LENGTH = 8192;
058
059 /**
060 * Byte buffer used for readFully(type[], int, int). Note that this
061 * array is also used for bulk reads in readShort(), readInt(), etc, so
062 * it should be large enough to hold a primitive value (i.e. >= 8 bytes).
063 * Also note that this array is package protected, so that it can be
064 * used by ImageOutputStreamImpl in a similar manner.
065 */
066 byte[] byteBuf = new byte[BYTE_BUF_LENGTH];
067
068 /**
069 * The byte order of the stream as an instance of the enumeration
070 * class <code>java.nio.ByteOrder</code>, where
071 * <code>ByteOrder.BIG_ENDIAN</code> indicates network byte order
072 * and <code>ByteOrder.LITTLE_ENDIAN</code> indicates the reverse
073 * order. By default, the value is
074 * <code>ByteOrder.BIG_ENDIAN</code>.
075 */
076 protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
077
078 /**
079 * The current read position within the stream. Subclasses are
080 * responsible for keeping this value current from any method they
081 * override that alters the read position.
082 */
083 protected long streamPos;
084
085 /**
086 * The current bit offset within the stream. Subclasses are
087 * responsible for keeping this value current from any method they
088 * override that alters the bit offset.
089 */
090 protected int bitOffset;
091
092 /**
093 * The position prior to which data may be discarded. Seeking
094 * to a smaller position is not allowed. <code>flushedPos</code>
095 * will always be >= 0.
096 */
097 protected long flushedPos = 0;
098
099 /**
100 * Constructs an <code>ImageInputStreamImpl</code>.
101 */
102 public ImageInputStreamImpl() {
103 }
104
105 /**
106 * Throws an <code>IOException</code> if the stream has been closed.
107 * Subclasses may call this method from any of their methods that
108 * require the stream not to be closed.
109 *
110 * @exception IOException if the stream is closed.
111 */
112 protected final void checkClosed() throws IOException {
113 if (isClosed) {
114 throw new IOException("closed");
115 }
116 }
117
118 public void setByteOrder(ByteOrder byteOrder) {
119 this .byteOrder = byteOrder;
120 }
121
122 public ByteOrder getByteOrder() {
123 return byteOrder;
124 }
125
126 /**
127 * Reads a single byte from the stream and returns it as an
128 * <code>int</code> between 0 and 255. If EOF is reached,
129 * <code>-1</code> is returned.
130 *
131 * <p> Subclasses must provide an implementation for this method.
132 * The subclass implementation should update the stream position
133 * before exiting.
134 *
135 * <p> The bit offset within the stream must be reset to zero before
136 * the read occurs.
137 *
138 * @return the value of the next byte in the stream, or <code>-1</code>
139 * if EOF is reached.
140 *
141 * @exception IOException if the stream has been closed.
142 */
143 public abstract int read() throws IOException;
144
145 /**
146 * A convenience method that calls <code>read(b, 0, b.length)</code>.
147 *
148 * <p> The bit offset within the stream is reset to zero before
149 * the read occurs.
150 *
151 * @return the number of bytes actually read, or <code>-1</code>
152 * to indicate EOF.
153 *
154 * @exception NullPointerException if <code>b</code> is
155 * <code>null</code>.
156 * @exception IOException if an I/O error occurs.
157 */
158 public int read(byte[] b) throws IOException {
159 return read(b, 0, b.length);
160 }
161
162 /**
163 * Reads up to <code>len</code> bytes from the stream, and stores
164 * them into <code>b</code> starting at index <code>off</code>.
165 * If no bytes can be read because the end of the stream has been
166 * reached, <code>-1</code> is returned.
167 *
168 * <p> The bit offset within the stream must be reset to zero before
169 * the read occurs.
170 *
171 * <p> Subclasses must provide an implementation for this method.
172 * The subclass implementation should update the stream position
173 * before exiting.
174 *
175 * @param b an array of bytes to be written to.
176 * @param off the starting position within <code>b</code> to write to.
177 * @param len the maximum number of bytes to read.
178 *
179 * @return the number of bytes actually read, or <code>-1</code>
180 * to indicate EOF.
181 *
182 * @exception IndexOutOfBoundsException if <code>off</code> is
183 * negative, <code>len</code> is negative, or <code>off +
184 * len</code> is greater than <code>b.length</code>.
185 * @exception NullPointerException if <code>b</code> is
186 * <code>null</code>.
187 * @exception IOException if an I/O error occurs.
188 */
189 public abstract int read(byte[] b, int off, int len)
190 throws IOException;
191
192 public void readBytes(IIOByteBuffer buf, int len)
193 throws IOException {
194 if (len < 0) {
195 throw new IndexOutOfBoundsException("len < 0!");
196 }
197 if (buf == null) {
198 throw new NullPointerException("buf == null!");
199 }
200
201 byte[] data = new byte[len];
202 len = read(data, 0, len);
203
204 buf.setData(data);
205 buf.setOffset(0);
206 buf.setLength(len);
207 }
208
209 public boolean readBoolean() throws IOException {
210 int ch = this .read();
211 if (ch < 0) {
212 throw new EOFException();
213 }
214 return (ch != 0);
215 }
216
217 public byte readByte() throws IOException {
218 int ch = this .read();
219 if (ch < 0) {
220 throw new EOFException();
221 }
222 return (byte) ch;
223 }
224
225 public int readUnsignedByte() throws IOException {
226 int ch = this .read();
227 if (ch < 0) {
228 throw new EOFException();
229 }
230 return ch;
231 }
232
233 public short readShort() throws IOException {
234 if (read(byteBuf, 0, 2) < 0) {
235 throw new EOFException();
236 }
237
238 if (byteOrder == ByteOrder.BIG_ENDIAN) {
239 return (short) (((byteBuf[0] & 0xff) << 8) | ((byteBuf[1] & 0xff) << 0));
240 } else {
241 return (short) (((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0));
242 }
243 }
244
245 public int readUnsignedShort() throws IOException {
246 return ((int) readShort()) & 0xffff;
247 }
248
249 public char readChar() throws IOException {
250 return (char) readShort();
251 }
252
253 public int readInt() throws IOException {
254 if (read(byteBuf, 0, 4) < 0) {
255 throw new EOFException();
256 }
257
258 if (byteOrder == ByteOrder.BIG_ENDIAN) {
259 return (((byteBuf[0] & 0xff) << 24)
260 | ((byteBuf[1] & 0xff) << 16)
261 | ((byteBuf[2] & 0xff) << 8) | ((byteBuf[3] & 0xff) << 0));
262 } else {
263 return (((byteBuf[3] & 0xff) << 24)
264 | ((byteBuf[2] & 0xff) << 16)
265 | ((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0));
266 }
267 }
268
269 public long readUnsignedInt() throws IOException {
270 return ((long) readInt()) & 0xffffffffL;
271 }
272
273 public long readLong() throws IOException {
274 // REMIND: Once 6277756 is fixed, we should do a bulk read of all 8
275 // bytes here as we do in readShort() and readInt() for even better
276 // performance (see 6347575 for details).
277 int i1 = readInt();
278 int i2 = readInt();
279
280 if (byteOrder == ByteOrder.BIG_ENDIAN) {
281 return ((long) i1 << 32) + (i2 & 0xFFFFFFFFL);
282 } else {
283 return ((long) i2 << 32) + (i1 & 0xFFFFFFFFL);
284 }
285 }
286
287 public float readFloat() throws IOException {
288 return Float.intBitsToFloat(readInt());
289 }
290
291 public double readDouble() throws IOException {
292 return Double.longBitsToDouble(readLong());
293 }
294
295 public String readLine() throws IOException {
296 StringBuffer input = new StringBuffer();
297 int c = -1;
298 boolean eol = false;
299
300 while (!eol) {
301 switch (c = read()) {
302 case -1:
303 case '\n':
304 eol = true;
305 break;
306 case '\r':
307 eol = true;
308 long cur = getStreamPosition();
309 if ((read()) != '\n') {
310 seek(cur);
311 }
312 break;
313 default:
314 input.append((char) c);
315 break;
316 }
317 }
318
319 if ((c == -1) && (input.length() == 0)) {
320 return null;
321 }
322 return input.toString();
323 }
324
325 public String readUTF() throws IOException {
326 this .bitOffset = 0;
327
328 // Fix 4494369: method ImageInputStreamImpl.readUTF()
329 // does not work as specified (it should always assume
330 // network byte order).
331 ByteOrder oldByteOrder = getByteOrder();
332 setByteOrder(ByteOrder.BIG_ENDIAN);
333
334 String ret;
335 try {
336 ret = DataInputStream.readUTF(this );
337 } catch (IOException e) {
338 // Restore the old byte order even if an exception occurs
339 setByteOrder(oldByteOrder);
340 throw e;
341 }
342
343 setByteOrder(oldByteOrder);
344 return ret;
345 }
346
347 public void readFully(byte[] b, int off, int len)
348 throws IOException {
349 // Fix 4430357 - if off + len < 0, overflow occurred
350 if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
351 throw new IndexOutOfBoundsException(
352 "off < 0 || len < 0 || off + len > b.length!");
353 }
354
355 while (len > 0) {
356 int nbytes = read(b, off, len);
357 if (nbytes == -1) {
358 throw new EOFException();
359 }
360 off += nbytes;
361 len -= nbytes;
362 }
363 }
364
365 public void readFully(byte[] b) throws IOException {
366 readFully(b, 0, b.length);
367 }
368
369 public void readFully(short[] s, int off, int len)
370 throws IOException {
371 // Fix 4430357 - if off + len < 0, overflow occurred
372 if (off < 0 || len < 0 || off + len > s.length || off + len < 0) {
373 throw new IndexOutOfBoundsException(
374 "off < 0 || len < 0 || off + len > s.length!");
375 }
376
377 while (len > 0) {
378 int nelts = Math.min(len, byteBuf.length / 2);
379 readFully(byteBuf, 0, nelts * 2);
380 toShorts(byteBuf, s, off, nelts);
381 off += nelts;
382 len -= nelts;
383 }
384 }
385
386 public void readFully(char[] c, int off, int len)
387 throws IOException {
388 // Fix 4430357 - if off + len < 0, overflow occurred
389 if (off < 0 || len < 0 || off + len > c.length || off + len < 0) {
390 throw new IndexOutOfBoundsException(
391 "off < 0 || len < 0 || off + len > c.length!");
392 }
393
394 while (len > 0) {
395 int nelts = Math.min(len, byteBuf.length / 2);
396 readFully(byteBuf, 0, nelts * 2);
397 toChars(byteBuf, c, off, nelts);
398 off += nelts;
399 len -= nelts;
400 }
401 }
402
403 public void readFully(int[] i, int off, int len) throws IOException {
404 // Fix 4430357 - if off + len < 0, overflow occurred
405 if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
406 throw new IndexOutOfBoundsException(
407 "off < 0 || len < 0 || off + len > i.length!");
408 }
409
410 while (len > 0) {
411 int nelts = Math.min(len, byteBuf.length / 4);
412 readFully(byteBuf, 0, nelts * 4);
413 toInts(byteBuf, i, off, nelts);
414 off += nelts;
415 len -= nelts;
416 }
417 }
418
419 public void readFully(long[] l, int off, int len)
420 throws IOException {
421 // Fix 4430357 - if off + len < 0, overflow occurred
422 if (off < 0 || len < 0 || off + len > l.length || off + len < 0) {
423 throw new IndexOutOfBoundsException(
424 "off < 0 || len < 0 || off + len > l.length!");
425 }
426
427 while (len > 0) {
428 int nelts = Math.min(len, byteBuf.length / 8);
429 readFully(byteBuf, 0, nelts * 8);
430 toLongs(byteBuf, l, off, nelts);
431 off += nelts;
432 len -= nelts;
433 }
434 }
435
436 public void readFully(float[] f, int off, int len)
437 throws IOException {
438 // Fix 4430357 - if off + len < 0, overflow occurred
439 if (off < 0 || len < 0 || off + len > f.length || off + len < 0) {
440 throw new IndexOutOfBoundsException(
441 "off < 0 || len < 0 || off + len > f.length!");
442 }
443
444 while (len > 0) {
445 int nelts = Math.min(len, byteBuf.length / 4);
446 readFully(byteBuf, 0, nelts * 4);
447 toFloats(byteBuf, f, off, nelts);
448 off += nelts;
449 len -= nelts;
450 }
451 }
452
453 public void readFully(double[] d, int off, int len)
454 throws IOException {
455 // Fix 4430357 - if off + len < 0, overflow occurred
456 if (off < 0 || len < 0 || off + len > d.length || off + len < 0) {
457 throw new IndexOutOfBoundsException(
458 "off < 0 || len < 0 || off + len > d.length!");
459 }
460
461 while (len > 0) {
462 int nelts = Math.min(len, byteBuf.length / 8);
463 readFully(byteBuf, 0, nelts * 8);
464 toDoubles(byteBuf, d, off, nelts);
465 off += nelts;
466 len -= nelts;
467 }
468 }
469
470 private void toShorts(byte[] b, short[] s, int off, int len) {
471 int boff = 0;
472 if (byteOrder == ByteOrder.BIG_ENDIAN) {
473 for (int j = 0; j < len; j++) {
474 int b0 = b[boff];
475 int b1 = b[boff + 1] & 0xff;
476 s[off + j] = (short) ((b0 << 8) | b1);
477 boff += 2;
478 }
479 } else {
480 for (int j = 0; j < len; j++) {
481 int b0 = b[boff + 1];
482 int b1 = b[boff] & 0xff;
483 s[off + j] = (short) ((b0 << 8) | b1);
484 boff += 2;
485 }
486 }
487 }
488
489 private void toChars(byte[] b, char[] c, int off, int len) {
490 int boff = 0;
491 if (byteOrder == ByteOrder.BIG_ENDIAN) {
492 for (int j = 0; j < len; j++) {
493 int b0 = b[boff];
494 int b1 = b[boff + 1] & 0xff;
495 c[off + j] = (char) ((b0 << 8) | b1);
496 boff += 2;
497 }
498 } else {
499 for (int j = 0; j < len; j++) {
500 int b0 = b[boff + 1];
501 int b1 = b[boff] & 0xff;
502 c[off + j] = (char) ((b0 << 8) | b1);
503 boff += 2;
504 }
505 }
506 }
507
508 private void toInts(byte[] b, int[] i, int off, int len) {
509 int boff = 0;
510 if (byteOrder == ByteOrder.BIG_ENDIAN) {
511 for (int j = 0; j < len; j++) {
512 int b0 = b[boff];
513 int b1 = b[boff + 1] & 0xff;
514 int b2 = b[boff + 2] & 0xff;
515 int b3 = b[boff + 3] & 0xff;
516 i[off + j] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
517 boff += 4;
518 }
519 } else {
520 for (int j = 0; j < len; j++) {
521 int b0 = b[boff + 3];
522 int b1 = b[boff + 2] & 0xff;
523 int b2 = b[boff + 1] & 0xff;
524 int b3 = b[boff] & 0xff;
525 i[off + j] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
526 boff += 4;
527 }
528 }
529 }
530
531 private void toLongs(byte[] b, long[] l, int off, int len) {
532 int boff = 0;
533 if (byteOrder == ByteOrder.BIG_ENDIAN) {
534 for (int j = 0; j < len; j++) {
535 int b0 = b[boff];
536 int b1 = b[boff + 1] & 0xff;
537 int b2 = b[boff + 2] & 0xff;
538 int b3 = b[boff + 3] & 0xff;
539 int b4 = b[boff + 4];
540 int b5 = b[boff + 5] & 0xff;
541 int b6 = b[boff + 6] & 0xff;
542 int b7 = b[boff + 7] & 0xff;
543
544 int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
545 int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
546
547 l[off + j] = ((long) i0 << 32) | (i1 & 0xffffffffL);
548 boff += 8;
549 }
550 } else {
551 for (int j = 0; j < len; j++) {
552 int b0 = b[boff + 7];
553 int b1 = b[boff + 6] & 0xff;
554 int b2 = b[boff + 5] & 0xff;
555 int b3 = b[boff + 4] & 0xff;
556 int b4 = b[boff + 3];
557 int b5 = b[boff + 2] & 0xff;
558 int b6 = b[boff + 1] & 0xff;
559 int b7 = b[boff] & 0xff;
560
561 int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
562 int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
563
564 l[off + j] = ((long) i0 << 32) | (i1 & 0xffffffffL);
565 boff += 8;
566 }
567 }
568 }
569
570 private void toFloats(byte[] b, float[] f, int off, int len) {
571 int boff = 0;
572 if (byteOrder == ByteOrder.BIG_ENDIAN) {
573 for (int j = 0; j < len; j++) {
574 int b0 = b[boff];
575 int b1 = b[boff + 1] & 0xff;
576 int b2 = b[boff + 2] & 0xff;
577 int b3 = b[boff + 3] & 0xff;
578 int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
579 f[off + j] = Float.intBitsToFloat(i);
580 boff += 4;
581 }
582 } else {
583 for (int j = 0; j < len; j++) {
584 int b0 = b[boff + 3];
585 int b1 = b[boff + 2] & 0xff;
586 int b2 = b[boff + 1] & 0xff;
587 int b3 = b[boff + 0] & 0xff;
588 int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
589 f[off + j] = Float.intBitsToFloat(i);
590 boff += 4;
591 }
592 }
593 }
594
595 private void toDoubles(byte[] b, double[] d, int off, int len) {
596 int boff = 0;
597 if (byteOrder == ByteOrder.BIG_ENDIAN) {
598 for (int j = 0; j < len; j++) {
599 int b0 = b[boff];
600 int b1 = b[boff + 1] & 0xff;
601 int b2 = b[boff + 2] & 0xff;
602 int b3 = b[boff + 3] & 0xff;
603 int b4 = b[boff + 4];
604 int b5 = b[boff + 5] & 0xff;
605 int b6 = b[boff + 6] & 0xff;
606 int b7 = b[boff + 7] & 0xff;
607
608 int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
609 int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
610 long l = ((long) i0 << 32) | (i1 & 0xffffffffL);
611
612 d[off + j] = Double.longBitsToDouble(l);
613 boff += 8;
614 }
615 } else {
616 for (int j = 0; j < len; j++) {
617 int b0 = b[boff + 7];
618 int b1 = b[boff + 6] & 0xff;
619 int b2 = b[boff + 5] & 0xff;
620 int b3 = b[boff + 4] & 0xff;
621 int b4 = b[boff + 3];
622 int b5 = b[boff + 2] & 0xff;
623 int b6 = b[boff + 1] & 0xff;
624 int b7 = b[boff] & 0xff;
625
626 int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
627 int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
628 long l = ((long) i0 << 32) | (i1 & 0xffffffffL);
629
630 d[off + j] = Double.longBitsToDouble(l);
631 boff += 8;
632 }
633 }
634 }
635
636 public long getStreamPosition() throws IOException {
637 checkClosed();
638 return streamPos;
639 }
640
641 public int getBitOffset() throws IOException {
642 checkClosed();
643 return bitOffset;
644 }
645
646 public void setBitOffset(int bitOffset) throws IOException {
647 checkClosed();
648 if (bitOffset < 0 || bitOffset > 7) {
649 throw new IllegalArgumentException(
650 "bitOffset must be betwwen 0 and 7!");
651 }
652 this .bitOffset = bitOffset;
653 }
654
655 public int readBit() throws IOException {
656 checkClosed();
657
658 // Compute final bit offset before we call read() and seek()
659 int newBitOffset = (this .bitOffset + 1) & 0x7;
660
661 int val = read();
662 if (val == -1) {
663 throw new EOFException();
664 }
665
666 if (newBitOffset != 0) {
667 // Move byte position back if in the middle of a byte
668 seek(getStreamPosition() - 1);
669 // Shift the bit to be read to the rightmost position
670 val >>= 8 - newBitOffset;
671 }
672 this .bitOffset = newBitOffset;
673
674 return val & 0x1;
675 }
676
677 public long readBits(int numBits) throws IOException {
678 checkClosed();
679
680 if (numBits < 0 || numBits > 64) {
681 throw new IllegalArgumentException();
682 }
683 if (numBits == 0) {
684 return 0L;
685 }
686
687 // Have to read additional bits on the left equal to the bit offset
688 int bitsToRead = numBits + bitOffset;
689
690 // Compute final bit offset before we call read() and seek()
691 int newBitOffset = (this .bitOffset + numBits) & 0x7;
692
693 // Read a byte at a time, accumulate
694 long accum = 0L;
695 while (bitsToRead > 0) {
696 int val = read();
697 if (val == -1) {
698 throw new EOFException();
699 }
700
701 accum <<= 8;
702 accum |= val;
703 bitsToRead -= 8;
704 }
705
706 // Move byte position back if in the middle of a byte
707 if (newBitOffset != 0) {
708 seek(getStreamPosition() - 1);
709 }
710 this .bitOffset = newBitOffset;
711
712 // Shift away unwanted bits on the right.
713 accum >>>= (-bitsToRead); // Negative of bitsToRead == extra bits read
714
715 // Mask out unwanted bits on the left
716 accum &= (-1L >>> (64 - numBits));
717
718 return accum;
719 }
720
721 /**
722 * Returns <code>-1L</code> to indicate that the stream has unknown
723 * length. Subclasses must override this method to provide actual
724 * length information.
725 *
726 * @return -1L to indicate unknown length.
727 */
728 public long length() {
729 return -1L;
730 }
731
732 /**
733 * Advances the current stream position by calling
734 * <code>seek(getStreamPosition() + n)</code>.
735 *
736 * <p> The bit offset is reset to zero.
737 *
738 * @param n the number of bytes to seek forward.
739 *
740 * @return an <code>int</code> representing the number of bytes
741 * skipped.
742 *
743 * @exception IOException if <code>getStreamPosition</code>
744 * throws an <code>IOException</code> when computing either
745 * the starting or ending position.
746 */
747 public int skipBytes(int n) throws IOException {
748 long pos = getStreamPosition();
749 seek(pos + n);
750 return (int) (getStreamPosition() - pos);
751 }
752
753 /**
754 * Advances the current stream position by calling
755 * <code>seek(getStreamPosition() + n)</code>.
756 *
757 * <p> The bit offset is reset to zero.
758 *
759 * @param n the number of bytes to seek forward.
760 *
761 * @return a <code>long</code> representing the number of bytes
762 * skipped.
763 *
764 * @exception IOException if <code>getStreamPosition</code>
765 * throws an <code>IOException</code> when computing either
766 * the starting or ending position.
767 */
768 public long skipBytes(long n) throws IOException {
769 long pos = getStreamPosition();
770 seek(pos + n);
771 return getStreamPosition() - pos;
772 }
773
774 public void seek(long pos) throws IOException {
775 checkClosed();
776
777 // This test also covers pos < 0
778 if (pos < flushedPos) {
779 throw new IndexOutOfBoundsException("pos < flushedPos!");
780 }
781
782 this .streamPos = pos;
783 this .bitOffset = 0;
784 }
785
786 /**
787 * Pushes the current stream position onto a stack of marked
788 * positions.
789 */
790 public void mark() {
791 try {
792 markByteStack.push(new Long(getStreamPosition()));
793 markBitStack.push(new Integer(getBitOffset()));
794 } catch (IOException e) {
795 }
796 }
797
798 /**
799 * Resets the current stream byte and bit positions from the stack
800 * of marked positions.
801 *
802 * <p> An <code>IOException</code> will be thrown if the previous
803 * marked position lies in the discarded portion of the stream.
804 *
805 * @exception IOException if an I/O error occurs.
806 */
807 public void reset() throws IOException {
808 if (markByteStack.empty()) {
809 return;
810 }
811
812 long pos = ((Long) markByteStack.pop()).longValue();
813 if (pos < flushedPos) {
814 throw new IIOException(
815 "Previous marked position has been discarded!");
816 }
817 seek(pos);
818
819 int offset = ((Integer) markBitStack.pop()).intValue();
820 setBitOffset(offset);
821 }
822
823 public void flushBefore(long pos) throws IOException {
824 checkClosed();
825 if (pos < flushedPos) {
826 throw new IndexOutOfBoundsException("pos < flushedPos!");
827 }
828 if (pos > getStreamPosition()) {
829 throw new IndexOutOfBoundsException(
830 "pos > getStreamPosition()!");
831 }
832 // Invariant: flushedPos >= 0
833 flushedPos = pos;
834 }
835
836 public void flush() throws IOException {
837 flushBefore(getStreamPosition());
838 }
839
840 public long getFlushedPosition() {
841 return flushedPos;
842 }
843
844 /**
845 * Default implementation returns false. Subclasses should
846 * override this if they cache data.
847 */
848 public boolean isCached() {
849 return false;
850 }
851
852 /**
853 * Default implementation returns false. Subclasses should
854 * override this if they cache data in main memory.
855 */
856 public boolean isCachedMemory() {
857 return false;
858 }
859
860 /**
861 * Default implementation returns false. Subclasses should
862 * override this if they cache data in a temporary file.
863 */
864 public boolean isCachedFile() {
865 return false;
866 }
867
868 public void close() throws IOException {
869 checkClosed();
870
871 isClosed = true;
872 }
873
874 /**
875 * Finalizes this object prior to garbage collection. The
876 * <code>close</code> method is called to close any open input
877 * source. This method should not be called from application
878 * code.
879 *
880 * @exception Throwable if an error occurs during superclass
881 * finalization.
882 */
883 protected void finalize() throws Throwable {
884 if (!isClosed) {
885 try {
886 close();
887 } catch (IOException e) {
888 }
889 }
890 super.finalize();
891 }
892 }
|