| /* ImageInputStream.java -- |
| Copyright (C) 2004 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.imageio.stream; |
| |
| import java.io.DataInputStream; |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.nio.ByteOrder; |
| import java.util.Stack; |
| |
| /** |
| * @author Michael Koch (konqueror@gmx.de) |
| */ |
| public abstract class ImageInputStreamImpl implements ImageInputStream |
| { |
| private boolean closed; |
| private Stack markStack = new Stack(); |
| |
| byte[] buffer = new byte[8]; |
| |
| protected int bitOffset; |
| protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; |
| protected long flushedPos; |
| protected long streamPos; |
| |
| public ImageInputStreamImpl() |
| { |
| // Do nothing here. |
| } |
| |
| protected final void checkClosed() |
| throws IOException |
| { |
| if (closed) |
| throw new IOException("stream closed"); |
| } |
| |
| public void close() |
| throws IOException |
| { |
| checkClosed(); |
| closed = true; |
| } |
| |
| protected void finalize() |
| throws Throwable |
| { |
| if (!closed) |
| close(); |
| } |
| |
| public void flush() |
| throws IOException |
| { |
| flushBefore(getStreamPosition()); |
| } |
| |
| public void flushBefore(long position) |
| throws IOException |
| { |
| if (position < flushedPos) |
| throw new IndexOutOfBoundsException(); |
| |
| if (position > streamPos) |
| throw new IndexOutOfBoundsException(); |
| |
| flushedPos = position; |
| } |
| |
| public int getBitOffset() |
| throws IOException |
| { |
| checkClosed(); |
| return bitOffset; |
| } |
| |
| public ByteOrder getByteOrder() |
| { |
| return byteOrder; |
| } |
| |
| public long getFlushedPosition() |
| { |
| return flushedPos; |
| } |
| |
| public long getStreamPosition() |
| throws IOException |
| { |
| checkClosed(); |
| return streamPos; |
| } |
| |
| public boolean isCached() |
| { |
| return false; |
| } |
| |
| public boolean isCachedFile() |
| { |
| return false; |
| } |
| |
| public boolean isCachedMemory() |
| { |
| return false; |
| } |
| |
| public long length() |
| { |
| return -1L; |
| } |
| |
| public void mark() |
| { |
| try |
| { |
| markStack.push(new Long(getStreamPosition())); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| public abstract int read() |
| throws IOException; |
| |
| public abstract int read(byte[] data, int offset, int len) |
| throws IOException; |
| |
| public int read(byte[] data) |
| throws IOException |
| { |
| return read(data, 0, data.length); |
| } |
| |
| public int readBit() |
| throws IOException |
| { |
| checkClosed(); |
| |
| // Calculate new bit offset here as readByte clears it. |
| int newOffset = (bitOffset + 1) & 0x7; |
| |
| // Clears bitOffset. |
| byte data = readByte(); |
| |
| // If newOffset is 0 it means we just read the 8th bit in a byte |
| // and therefore we want to advance to the next byte. Otherwise |
| // we want to roll back the stream one byte so that future readBit |
| // calls read bits from the same current byte. |
| if (newOffset != 0) |
| { |
| seek(getStreamPosition() - 1); |
| data = (byte) (data >> (8 - newOffset)); |
| } |
| |
| bitOffset = newOffset; |
| return data & 0x1; |
| } |
| |
| public long readBits(int numBits) |
| throws IOException |
| { |
| checkClosed(); |
| |
| if (numBits < 0 || numBits > 64) |
| throw new IllegalArgumentException(); |
| |
| long bits = 0L; |
| |
| for (int i = 0; i < numBits; i++) |
| { |
| bits <<= 1; |
| bits |= readBit(); |
| } |
| return bits; |
| } |
| |
| public boolean readBoolean() |
| throws IOException |
| { |
| byte data = readByte(); |
| |
| return data != 0; |
| } |
| |
| public byte readByte() |
| throws IOException |
| { |
| checkClosed(); |
| |
| int data = read(); |
| |
| if (data == -1) |
| throw new EOFException(); |
| |
| return (byte) data; |
| } |
| |
| public void readBytes(IIOByteBuffer buffer, int len) |
| throws IOException |
| { |
| readFullyPrivate(buffer.getData(), buffer.getOffset(), len); |
| |
| buffer.setLength(len); |
| } |
| |
| public char readChar() |
| throws IOException |
| { |
| return (char) readShort(); |
| } |
| |
| public double readDouble() |
| throws IOException |
| { |
| return Double.longBitsToDouble(readLong()); |
| } |
| |
| public float readFloat() |
| throws IOException |
| { |
| return Float.intBitsToFloat(readInt()); |
| } |
| |
| public void readFully(byte[] data) |
| throws IOException |
| { |
| readFully(data, 0, data.length); |
| } |
| |
| public void readFully(byte[] data, int offset, int len) |
| throws IOException |
| { |
| readFullyPrivate(data, offset, len); |
| } |
| |
| public void readFully(char[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readChar(); |
| } |
| |
| public void readFully(double[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readDouble(); |
| } |
| |
| public void readFully(float[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readFloat(); |
| } |
| |
| public void readFully(int[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readInt(); |
| } |
| |
| public void readFully(long[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readLong(); |
| } |
| |
| public void readFully(short[] data, int offset, int len) |
| throws IOException |
| { |
| for (int i = 0; i < len; ++i) |
| data[offset + i] = readShort(); |
| } |
| |
| public int readInt() |
| throws IOException |
| { |
| readFullyPrivate(buffer, 0, 4); |
| |
| if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) |
| return (int) |
| (((int) (buffer[0] & 0xff) << 0) |
| | ((int) (buffer[1] & 0xff) << 8) |
| | ((int) (buffer[2] & 0xff) << 16) |
| | ((int) (buffer[3] & 0xff) << 24)); |
| |
| return (int) |
| (((int) (buffer[0] & 0xff) << 24) |
| + ((int) (buffer[1] & 0xff) << 16) |
| + ((int) (buffer[2] & 0xff) << 8) |
| + ((int) (buffer[3] & 0xff) << 0)); |
| } |
| |
| public String readLine() |
| throws IOException |
| { |
| checkClosed(); |
| |
| int c = -1; |
| boolean eol = false; |
| StringBuffer buffer = new StringBuffer(); |
| |
| c = read(); |
| if (c == -1) |
| return null; |
| |
| while (!eol) |
| { |
| switch(c) |
| { |
| case '\r': |
| // Check for following '\n'. |
| long oldPosition = getStreamPosition(); |
| c = read(); |
| if (c == -1 || c == '\n') |
| eol = true; |
| else |
| { |
| seek(oldPosition); |
| eol = true; |
| } |
| continue; |
| |
| case '\n': |
| eol = true; |
| continue; |
| |
| default: |
| buffer.append((char) c); |
| break; |
| } |
| c = read(); |
| if (c == -1) |
| eol = true; |
| } |
| |
| return buffer.toString(); |
| } |
| |
| public long readLong() |
| throws IOException |
| { |
| readFullyPrivate(buffer, 0, 8); |
| |
| if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) |
| return (long) |
| (((long) (buffer[0] & 0xff) << 0) |
| | ((long) (buffer[1] & 0xff) << 8) |
| | ((long) (buffer[2] & 0xff) << 16) |
| | ((long) (buffer[3] & 0xff) << 24) |
| | ((long) (buffer[4] & 0xff) << 32) |
| | ((long) (buffer[5] & 0xff) << 40) |
| | ((long) (buffer[6] & 0xff) << 48) |
| | ((long) (buffer[7] & 0xff) << 56)); |
| |
| return (long) |
| (((long) (buffer[0] & 0xff) << 56) |
| | ((long) (buffer[1] & 0xff) << 48) |
| | ((long) (buffer[2] & 0xff) << 40) |
| | ((long) (buffer[3] & 0xff) << 32) |
| | ((long) (buffer[4] & 0xff) << 24) |
| | ((long) (buffer[5] & 0xff) << 16) |
| | ((long) (buffer[6] & 0xff) << 8) |
| | ((long) (buffer[7] & 0xff) << 0)); |
| } |
| |
| public short readShort() |
| throws IOException |
| { |
| readFullyPrivate(buffer, 0, 2); |
| |
| if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) |
| return (short) |
| (((short) (buffer[0] & 0xff) << 0) |
| | ((short) (buffer[1] & 0xff) << 8)); |
| |
| return (short) |
| (((short) (buffer[0] & 0xff) << 8) |
| | ((short) (buffer[1] & 0xff) << 0)); |
| } |
| |
| public int readUnsignedByte() |
| throws IOException |
| { |
| return (int) readByte() & 0xff; |
| } |
| |
| public long readUnsignedInt() |
| throws IOException |
| { |
| return (long) readInt() & 0xffffffffL; |
| } |
| |
| public int readUnsignedShort() |
| throws IOException |
| { |
| return (int) readShort() & 0xffff; |
| } |
| |
| public String readUTF() |
| throws IOException |
| { |
| checkClosed(); |
| |
| String data; |
| ByteOrder old = getByteOrder(); |
| // Strings are always big endian. |
| setByteOrder(ByteOrder.BIG_ENDIAN); |
| |
| try |
| { |
| data = DataInputStream.readUTF(this); |
| } |
| finally |
| { |
| setByteOrder(old); |
| } |
| |
| return data; |
| } |
| |
| public void reset() |
| throws IOException |
| { |
| checkClosed(); |
| |
| long mark = ((Long) markStack.pop()).longValue(); |
| seek(mark); |
| } |
| |
| public void seek(long position) |
| throws IOException |
| { |
| checkClosed(); |
| |
| if (position < getFlushedPosition()) |
| throw new IndexOutOfBoundsException("position < flushed position"); |
| |
| streamPos = position; |
| bitOffset = 0; |
| } |
| |
| public void setBitOffset (int bitOffset) |
| throws IOException |
| { |
| checkClosed(); |
| |
| if (bitOffset < 0 || bitOffset > 7) |
| throw new IllegalArgumentException("bitOffset not between 0 and 7 inclusive"); |
| |
| this.bitOffset = bitOffset; |
| } |
| |
| public void setByteOrder(ByteOrder byteOrder) |
| { |
| this.byteOrder = byteOrder; |
| } |
| |
| public int skipBytes(int num) |
| throws IOException |
| { |
| checkClosed(); |
| |
| seek(getStreamPosition() + num); |
| bitOffset = 0; |
| return num; |
| } |
| |
| public long skipBytes(long num) |
| throws IOException |
| { |
| checkClosed(); |
| |
| seek(getStreamPosition() + num); |
| bitOffset = 0; |
| return num; |
| } |
| |
| private void readFullyPrivate (byte[] buf, int offset, int len) throws IOException |
| { |
| checkClosed(); |
| |
| if (len < 0) |
| throw new IndexOutOfBoundsException("Negative length: " + len); |
| |
| while (len > 0) |
| { |
| // read will block until some data is available. |
| int numread = read (buf, offset, len); |
| if (numread < 0) |
| throw new EOFException (); |
| len -= numread; |
| offset += numread; |
| } |
| bitOffset = 0; |
| } |
| } |