001    /* ImageInputStream.java --
002       Copyright (C) 2004  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.imageio.stream;
040    
041    import gnu.java.lang.CPStringBuilder;
042    
043    import java.io.DataInputStream;
044    import java.io.EOFException;
045    import java.io.IOException;
046    import java.nio.ByteOrder;
047    import java.util.Stack;
048    
049    /**
050     * @author Michael Koch (konqueror@gmx.de)
051     */
052    public 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    }