001/* ImageInputStream.java --
002   Copyright (C) 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.imageio.stream;
040
041import gnu.java.lang.CPStringBuilder;
042
043import java.io.DataInputStream;
044import java.io.EOFException;
045import java.io.IOException;
046import java.nio.ByteOrder;
047import java.util.Stack;
048
049/**
050 * @author Michael Koch (konqueror@gmx.de)
051 */
052public abstract class ImageInputStreamImpl implements ImageInputStream
053{
054  private boolean closed;
055  private Stack markStack = new Stack();
056
057  byte[] buffer = new byte[8];
058
059  protected int bitOffset;
060  protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
061  protected long flushedPos;
062  protected long streamPos;
063
064  public ImageInputStreamImpl()
065  {
066    // Do nothing here.
067  }
068
069  protected final void checkClosed()
070    throws IOException
071  {
072    if (closed)
073      throw new IOException("stream closed");
074  }
075
076  public void close()
077    throws IOException
078  {
079    checkClosed();
080    closed = true;
081  }
082
083  protected void finalize()
084    throws Throwable
085  {
086    if (!closed)
087      close();
088  }
089
090  public void flush()
091    throws IOException
092  {
093    flushBefore(getStreamPosition());
094  }
095
096  public void flushBefore(long position)
097    throws IOException
098  {
099    if (position < flushedPos)
100      throw new IndexOutOfBoundsException();
101
102    if (position > streamPos)
103      throw new IndexOutOfBoundsException();
104
105    flushedPos = position;
106  }
107
108  public int getBitOffset()
109    throws IOException
110  {
111    checkClosed();
112    return bitOffset;
113  }
114
115  public ByteOrder getByteOrder()
116  {
117    return byteOrder;
118  }
119
120  public long getFlushedPosition()
121  {
122    return flushedPos;
123  }
124
125  public long getStreamPosition()
126    throws IOException
127  {
128    checkClosed();
129    return streamPos;
130  }
131
132  public boolean isCached()
133  {
134    return false;
135  }
136
137  public boolean isCachedFile()
138  {
139    return false;
140  }
141
142  public boolean isCachedMemory()
143  {
144    return false;
145  }
146
147  public long length()
148  {
149    return -1L;
150  }
151
152  public void mark()
153  {
154    try
155      {
156        markStack.push(new Long(getStreamPosition()));
157      }
158    catch (IOException e)
159      {
160        throw new RuntimeException(e);
161      }
162  }
163
164  public abstract int read()
165    throws IOException;
166
167  public abstract int read(byte[] data, int offset, int len)
168    throws IOException;
169
170  public int read(byte[] data)
171    throws IOException
172  {
173    return read(data, 0, data.length);
174  }
175
176  public int readBit()
177    throws IOException
178  {
179    checkClosed();
180
181    // Calculate new bit offset here as readByte clears it.
182    int newOffset = (bitOffset + 1) & 0x7;
183
184    // Clears bitOffset.
185    byte data = readByte();
186
187    // If newOffset is 0 it means we just read the 8th bit in a byte
188    // and therefore we want to advance to the next byte.  Otherwise
189    // we want to roll back the stream one byte so that future readBit
190    // calls read bits from the same current byte.
191    if (newOffset != 0)
192      {
193        seek(getStreamPosition() - 1);
194        data = (byte) (data >> (8 - newOffset));
195      }
196
197    bitOffset = newOffset;
198    return data & 0x1;
199  }
200
201  public long readBits(int numBits)
202    throws IOException
203  {
204    checkClosed();
205
206    if (numBits < 0 || numBits > 64)
207      throw new IllegalArgumentException();
208
209    long bits = 0L;
210
211    for (int i = 0; i < numBits; i++)
212      {
213        bits <<= 1;
214        bits |= readBit();
215      }
216    return bits;
217  }
218
219  public boolean readBoolean()
220    throws IOException
221  {
222    byte data = readByte();
223
224    return data != 0;
225  }
226
227  public byte readByte()
228    throws IOException
229  {
230    checkClosed();
231
232    int data = read();
233
234    if (data == -1)
235      throw new EOFException();
236
237    return (byte) data;
238  }
239
240  public void readBytes(IIOByteBuffer buffer, int len)
241    throws IOException
242  {
243    readFullyPrivate(buffer.getData(), buffer.getOffset(), len);
244
245    buffer.setLength(len);
246  }
247
248  public char readChar()
249    throws IOException
250  {
251    return (char) readShort();
252  }
253
254  public double readDouble()
255    throws IOException
256  {
257    return Double.longBitsToDouble(readLong());
258  }
259
260  public float readFloat()
261    throws IOException
262  {
263    return Float.intBitsToFloat(readInt());
264  }
265
266  public void readFully(byte[] data)
267    throws IOException
268  {
269    readFully(data, 0, data.length);
270  }
271
272  public void readFully(byte[] data, int offset, int len)
273    throws IOException
274  {
275    readFullyPrivate(data, offset, len);
276  }
277
278  public void readFully(char[] data, int offset, int len)
279    throws IOException
280  {
281    for (int i = 0; i < len; ++i)
282      data[offset + i] = readChar();
283  }
284
285  public void readFully(double[] data, int offset, int len)
286    throws IOException
287  {
288    for (int i = 0; i < len; ++i)
289      data[offset + i] = readDouble();
290  }
291
292  public void readFully(float[] data, int offset, int len)
293    throws IOException
294  {
295    for (int i = 0; i < len; ++i)
296      data[offset + i] = readFloat();
297  }
298
299  public void readFully(int[] data, int offset, int len)
300    throws IOException
301  {
302    for (int i = 0; i < len; ++i)
303      data[offset + i] = readInt();
304  }
305
306  public void readFully(long[] data, int offset, int len)
307    throws IOException
308  {
309    for (int i = 0; i < len; ++i)
310      data[offset + i] = readLong();
311  }
312
313  public void readFully(short[] data, int offset, int len)
314    throws IOException
315  {
316    for (int i = 0; i < len; ++i)
317      data[offset + i] = readShort();
318  }
319
320  public int readInt()
321    throws IOException
322  {
323    readFullyPrivate(buffer, 0, 4);
324
325    if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
326      return (int)
327        (((int) (buffer[0] & 0xff) << 0)
328         | ((int) (buffer[1] & 0xff) << 8)
329         | ((int) (buffer[2] & 0xff) << 16)
330         | ((int) (buffer[3] & 0xff) << 24));
331
332    return (int)
333      (((int) (buffer[0] & 0xff) << 24)
334       + ((int) (buffer[1] & 0xff) << 16)
335       + ((int) (buffer[2] & 0xff) << 8)
336       + ((int) (buffer[3] & 0xff) << 0));
337  }
338
339  public String readLine()
340    throws IOException
341  {
342    checkClosed();
343
344    int c = -1;
345    boolean eol = false;
346    CPStringBuilder buffer = new CPStringBuilder();
347
348    c = read();
349    if (c == -1)
350      return null;
351
352    while (!eol)
353      {
354        switch(c)
355          {
356          case '\r':
357            // Check for following '\n'.
358            long oldPosition = getStreamPosition();
359            c = read();
360            if (c == -1 || c == '\n')
361              eol = true;
362            else
363              {
364                seek(oldPosition);
365                eol = true;
366              }
367            continue;
368
369          case '\n':
370            eol = true;
371            continue;
372
373          default:
374            buffer.append((char) c);
375            break;
376          }
377        c = read();
378        if (c == -1)
379          eol = true;
380      }
381
382    return buffer.toString();
383  }
384
385  public long readLong()
386    throws IOException
387  {
388    readFullyPrivate(buffer, 0, 8);
389
390    if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
391      return (long)
392        (((long) (buffer[0] & 0xff) << 0)
393         | ((long) (buffer[1] & 0xff) << 8)
394         | ((long) (buffer[2] & 0xff) << 16)
395         | ((long) (buffer[3] & 0xff) << 24)
396         | ((long) (buffer[4] & 0xff) << 32)
397         | ((long) (buffer[5] & 0xff) << 40)
398         | ((long) (buffer[6] & 0xff) << 48)
399         | ((long) (buffer[7] & 0xff) << 56));
400
401    return  (long)
402      (((long) (buffer[0] & 0xff) << 56)
403       | ((long) (buffer[1] & 0xff) << 48)
404       | ((long) (buffer[2] & 0xff) << 40)
405       | ((long) (buffer[3] & 0xff) << 32)
406       | ((long) (buffer[4] & 0xff) << 24)
407       | ((long) (buffer[5] & 0xff) << 16)
408       | ((long) (buffer[6] & 0xff) << 8)
409       | ((long) (buffer[7] & 0xff) << 0));
410  }
411
412  public short readShort()
413    throws IOException
414  {
415    readFullyPrivate(buffer, 0, 2);
416
417    if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
418      return (short)
419        (((short) (buffer[0] & 0xff) << 0)
420         | ((short) (buffer[1] & 0xff) << 8));
421
422    return (short)
423      (((short) (buffer[0] & 0xff) << 8)
424       | ((short) (buffer[1] & 0xff) << 0));
425  }
426
427  public int readUnsignedByte()
428    throws IOException
429  {
430    return (int) readByte() & 0xff;
431  }
432
433  public long readUnsignedInt()
434    throws IOException
435  {
436    return (long) readInt() & 0xffffffffL;
437  }
438
439  public int readUnsignedShort()
440    throws IOException
441  {
442    return (int) readShort() & 0xffff;
443  }
444
445  public String readUTF()
446    throws IOException
447  {
448    checkClosed();
449
450    String data;
451    ByteOrder old = getByteOrder();
452    // Strings are always big endian.
453    setByteOrder(ByteOrder.BIG_ENDIAN);
454
455    try
456      {
457        data = DataInputStream.readUTF(this);
458      }
459    finally
460      {
461        setByteOrder(old);
462      }
463
464    return data;
465  }
466
467  public void reset()
468    throws IOException
469  {
470    checkClosed();
471
472    long mark = ((Long) markStack.pop()).longValue();
473    seek(mark);
474  }
475
476  public void seek(long position)
477    throws IOException
478  {
479    checkClosed();
480
481    if (position < getFlushedPosition())
482      throw new IndexOutOfBoundsException("position < flushed position");
483
484    streamPos = position;
485    bitOffset = 0;
486  }
487
488  public void setBitOffset (int bitOffset)
489    throws IOException
490  {
491    checkClosed();
492
493    if (bitOffset < 0 || bitOffset > 7)
494      throw new IllegalArgumentException("bitOffset not between 0 and 7 inclusive");
495
496    this.bitOffset = bitOffset;
497  }
498
499  public void setByteOrder(ByteOrder byteOrder)
500  {
501    this.byteOrder = byteOrder;
502  }
503
504  public int skipBytes(int num)
505    throws IOException
506  {
507    checkClosed();
508
509    seek(getStreamPosition() + num);
510    bitOffset = 0;
511    return num;
512  }
513
514  public long skipBytes(long num)
515    throws IOException
516  {
517    checkClosed();
518
519    seek(getStreamPosition() + num);
520    bitOffset = 0;
521    return num;
522  }
523
524  private void readFullyPrivate (byte[] buf, int offset, int len) throws IOException
525  {
526    checkClosed();
527
528    if (len < 0)
529      throw new IndexOutOfBoundsException("Negative length: " + len);
530
531    while (len > 0)
532      {
533        // read will block until some data is available.
534        int numread = read (buf, offset, len);
535        if (numread < 0)
536          throw new EOFException ();
537        len -= numread;
538        offset += numread;
539      }
540    bitOffset = 0;
541  }
542}