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