001/*
002 * $RCSfile: BufferedRandomAccessFile.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:16 $
005 * $State: Exp $
006 *
007 * Interface:           RandomAccessIO.java
008 *
009 * Description:         Abstract class for buffered random access I/O.
010 *
011 *
012 *
013 * COPYRIGHT:
014 *
015 * This software module was originally developed by Raphaël Grosbois and
016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019 * Centre France S.A) in the course of development of the JPEG2000
020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021 * software module is an implementation of a part of the JPEG 2000
022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024 * Partners) agree not to assert against ISO/IEC and users of the JPEG
025 * 2000 Standard (Users) any of their rights under the copyright, not
026 * including other intellectual property rights, for this software module
027 * with respect to the usage by ISO/IEC and Users of this software module
028 * or modifications thereof for use in hardware or software products
029 * claiming conformance to the JPEG 2000 Standard. Those intending to use
030 * this software module in hardware or software products are advised that
031 * their use may infringe existing patents. The original developers of
032 * this software module, JJ2000 Partners and ISO/IEC assume no liability
033 * for use of this software module or modifications thereof. No license
034 * or right to this software module is granted for non JPEG 2000 Standard
035 * conforming products. JJ2000 Partners have full right to use this
036 * software module for his/her own purpose, assign or donate this
037 * software module to any third party and to inhibit third parties from
038 * using this software module for non JPEG 2000 Standard conforming
039 * products. This copyright notice must be included in all copies or
040 * derivative works of this software module.
041 *
042 * Copyright (c) 1999/2000 JJ2000 Partners.
043 */
044
045package jj2000.j2k.io;
046
047import java.io.*;
048
049/**
050 * This class defines a Buffered Random Access File.  It implements the
051 * <tt>BinaryDataInput</tt> and <tt>BinaryDataOutput</tt> interfaces so that
052 * binary data input/output can be performed. This class is abstract since no
053 * assumption is done about the byte ordering type (little Endian, big
054 * Endian). So subclasses will have to implement methods like
055 * <tt>readShort()</tt>, <tt>writeShort()</tt>, <tt>readFloat()</tt>, ...
056 *
057 * <P><tt>BufferedRandomAccessFile</tt> (BRAF for short) is a
058 * <tt>RandomAccessFile</tt> containing an extra buffer. When the BRAF is
059 * accessed, it checks if the requested part of the file is in the buffer or
060 * not. If that is the case, the read/write is done on the buffer. If not, the
061 * file is uppdated to reflect the current status of the buffer and the file
062 * is then accessed for a new buffer containing the requested byte/bit.
063 *
064 * @see RandomAccessIO
065 * @see BinaryDataOutput
066 * @see BinaryDataInput
067 * @see BEBufferedRandomAccessFile
068 * */
069public abstract class BufferedRandomAccessFile
070    implements RandomAccessIO, EndianType {
071
072    /**
073     * The name of the current file
074     * */
075    private String fileName;
076
077    /**
078     * Whether the opened file is read only or not (defined by the constructor
079     * arguments)
080     * */
081    private boolean isReadOnly = true;
082
083    /**
084     * The RandomAccessFile associated with the buffer
085     * */
086    private RandomAccessFile theFile;
087
088    /**
089     * Buffer of bytes containing the part of the file that is currently being
090     * accessed
091     * */
092    protected byte[] byteBuffer;
093
094    /**
095     * Boolean keeping track of whether the byte buffer has been changed since
096     * it was read.
097     * */
098    protected boolean byteBufferChanged;
099
100    /**
101     * The current offset of the buffer (which will differ from the offset of
102     * the file)
103     * */
104    protected int offset;
105
106    /**
107     * The current position in the byte-buffer
108     * */
109    protected int pos;
110
111    /**
112     * The maximum number of bytes that can be read from the buffer
113     * */
114    protected int maxByte;
115
116    /**
117     * Whether the end of the file is in the current buffer or not
118     * */
119    protected boolean isEOFInBuffer;
120
121    /* The endianess of the class */
122    protected int byteOrdering;
123
124    /**
125     * Constructor. Always needs a size for the buffer.
126     *
127     * @param file The file associated with the buffer
128     *
129     * @param mode "r" for read, "rw" or "rw+" for read and write mode ("rw+"
130     *             opens the file for update whereas "rw" removes it
131     *             before. So the 2 modes are different only if the file
132     *             already exists).
133     *
134     * @param bufferSize The number of bytes to buffer
135     *
136     * @exception java.io.IOException If an I/O error ocurred.
137     * */
138    protected BufferedRandomAccessFile(File file,
139                                       String mode,
140                                       int bufferSize) throws IOException{
141
142        fileName = file.getName();
143        if(mode.equals("rw") || mode.equals("rw+")){ // mode read / write
144            isReadOnly = false;
145            if(mode.equals("rw")){ // mode read / (over)write
146                if(file.exists()) // Output file already exists
147                    file.delete();
148            }
149            mode = "rw";
150        }
151        theFile=new RandomAccessFile(file,mode);
152        byteBuffer=new byte[bufferSize];
153        readNewBuffer(0);
154    }
155
156    /**
157     * Constructor. Uses the default value for the byte-buffer
158     * size (512 bytes).
159     *
160     * @param file The file associated with the buffer
161     *
162     * @param mode "r" for read, "rw" or "rw+" for read and write mode
163     *             ("rw+" opens the file for update whereas "rw" removes
164     *             it before. So the 2 modes are different only if the
165     *             file already exists).
166     *
167     * @exception java.io.IOException If an I/O error ocurred.
168     * */
169    protected BufferedRandomAccessFile(File file,
170                                       String mode ) throws IOException{
171
172        this(file, mode, 512);
173    }
174
175    /**
176     * Constructor. Always needs a size for the buffer.
177     *
178     * @param name The name of the file associated with the buffer
179     *
180     * @param mode "r" for read, "rw" or "rw+" for read and write mode
181     *             ("rw+" opens the file for update whereas "rw" removes
182     *             it before. So the 2 modes are different only if the
183     *             file already exists).
184     *
185     * @param bufferSize The number of bytes to buffer
186     *
187     * @exception java.io.IOException If an I/O error ocurred.
188     * */
189    protected BufferedRandomAccessFile(String name,
190                                       String mode,
191                                       int bufferSize) throws IOException{
192        this(new File(name), mode, bufferSize);
193    }
194
195    /**
196     * Constructor. Uses the default value for the byte-buffer
197     * size (512 bytes).
198     *
199     * @param name The name of the file associated with the buffer
200     *
201     * @param mode "r" for read, "rw" or "rw+" for read and write mode
202     *             ("rw+" opens the file for update whereas "rw" removes
203     *             it before. So the 2 modes are different only if the
204     *             file already exists).
205     *
206     * @exception java.io.IOException If an I/O error ocurred.
207     * */
208    protected BufferedRandomAccessFile(String name,
209                                       String mode ) throws IOException{
210
211        this(name, mode, 512);
212    }
213
214    /**
215     * Reads a new buffer from the file. If there has been any
216     * changes made since the buffer was read, the buffer is
217     * first written to the file.
218     *
219     * @param off The offset where to move to.
220     *
221     * @exception java.io.IOException If an I/O error ocurred.
222     * */
223    protected final void readNewBuffer(int off) throws IOException{
224
225        /* If the buffer have changed. We need to write it to
226         * the file before reading a new buffer.
227         */
228        if(byteBufferChanged){
229            flush();
230        }
231        // Don't allow to seek beyond end of file if reading only
232        if (isReadOnly && off >= theFile.length()) {
233            throw new EOFException();
234        }
235        // Set new offset
236        offset = off;
237
238        theFile.seek(offset);
239
240        maxByte = theFile.read(byteBuffer,0,byteBuffer.length);
241        pos=0;
242
243        if(maxByte<byteBuffer.length){ // Not enough data in input file.
244            isEOFInBuffer = true;
245            if(maxByte==-1){
246                maxByte++;
247            }
248        }else{
249            isEOFInBuffer = false;
250        }
251    }
252
253    /**
254     * Closes the buffered random access file
255     *
256     * @exception java.io.IOException If an I/O error ocurred.
257     * */
258    public void close() throws IOException{
259        /* If the buffer has been changed, it need to be saved before
260         * closing
261         */
262        flush();
263        byteBuffer = null; // Release the byte-buffer reference
264        theFile.close();
265    }
266
267    /**
268     * Returns the current offset in the file
269     * */
270    public int getPos(){
271        return (offset+pos);
272    }
273
274    /**
275     * Returns the current length of the stream, in bytes, taking into
276     * account any buffering.
277     *
278     * @return The length of the stream, in bytes.
279     *
280     * @exception java.io.IOException If an I/O error ocurred.
281     * */
282    public int length() throws IOException{
283        int len;
284
285        len = (int)theFile.length();
286
287        // If the position in the buffer is not past the end of the file,
288        // the length of theFile is the length of the stream
289        if( (offset+maxByte)<=len ){
290            return(len);
291        }
292        else{ // If not, the file is extended due to the buffering
293            return (offset+maxByte);
294        }
295    }
296
297    /**
298     * Moves the current position to the given offset at which the
299     * next read or write occurs. The offset is measured from the
300     * beginning of the stream.
301     *
302     * @param off The offset where to move to.
303     *
304     * @exception EOFException If in read-only and seeking beyond EOF.
305     *
306     * @exception java.io.IOException If an I/O error ocurred.
307     * */
308    public void seek(int off) throws IOException{
309        /* If the new offset is within the buffer, only the pos value needs
310         * to be modified. Else, the buffer must be moved. */
311        if( (off>=offset)&&(off<(offset+byteBuffer.length)) ){
312            if (isReadOnly && isEOFInBuffer && off > offset+maxByte) {
313                // We are seeking beyond EOF in read-only mode!
314                throw new EOFException();
315            }
316            pos = off-offset;
317        }
318        else{
319            readNewBuffer(off);
320        }
321    }
322
323    /**
324     * Reads an unsigned byte of data from the stream. Prior to reading, the
325     * stream is realigned at the byte level.
326     *
327     * @return The byte read.
328     *
329     * @exception java.io.IOException If an I/O error ocurred.
330     *
331     * @exception java.io.EOFException If the end of file was reached
332     * */
333    public final int read() throws IOException, EOFException{
334        if(pos<maxByte){ // The byte can be read from the buffer
335            // In Java, the bytes are always signed.
336            return (byteBuffer[pos++]&0xFF);
337        }
338        else if(isEOFInBuffer){ // EOF is reached
339            pos = maxByte+1; // Set position to EOF
340            throw new EOFException();
341        }
342        else { // End of the buffer is reached
343            readNewBuffer(offset+pos);
344            return read();
345        }
346    }
347
348    /**
349     * Reads up to len bytes of data from this file into an array of
350     * bytes. This method reads repeatedly from the stream until all the bytes
351     * are read. This method blocks until all the bytes are read, the end of
352     * the stream is detected, or an exception is thrown.
353     *
354     * @param b The buffer into which the data is to be read. It must be long
355     * enough.
356     *
357     * @param off The index in 'b' where to place the first byte read.
358     *
359     * @param len The number of bytes to read.
360     *
361     * @exception EOFException If the end-of file was reached before
362     * getting all the necessary data.
363     *
364     * @exception IOException If an I/O error ocurred.
365     * */
366    public final void readFully(byte b[], int off, int len)
367        throws IOException {
368        int clen; // current length to read
369        while (len > 0) {
370            // There still is some data to read
371            if (pos<maxByte) { // We can read some data from buffer
372                clen = maxByte-pos;
373                if (clen > len) clen = len;
374                System.arraycopy(byteBuffer,pos,b,off,clen);
375                pos += clen;
376                off += clen;
377                len -= clen;
378            }
379            else if (isEOFInBuffer) {
380                pos = maxByte+1; // Set position to EOF
381                throw new EOFException();
382            }
383            else { // Buffer empty => get more data
384                readNewBuffer(offset+pos);
385            }
386        }
387    }
388
389    /**
390     * Writes a byte to the stream. Prior to writing, the stream is
391     * realigned at the byte level.
392     *
393     * @param b The byte to write. The lower 8 bits of <tt>b</tt> are
394     * written.
395     *
396     * @exception java.io.IOException If an I/O error ocurred.
397     * */
398    public final void write(int b) throws IOException{
399        // As long as pos is less than the length of the buffer we can write
400        // to the buffer. If the position is after the buffer a new buffer is
401        // needed
402        if(pos<byteBuffer.length){
403            if(isReadOnly)
404                throw new IOException("File is read only");
405            byteBuffer[pos]=(byte)b;
406            if(pos>=maxByte){
407                maxByte=pos+1;
408            }
409            pos++;
410            byteBufferChanged =true;
411        }
412        else{
413            readNewBuffer(offset+pos);
414            write(b);
415        }
416    }
417
418    /**
419     * Writes a byte to the stream. Prior to writing, the stream is
420     * realigned at the byte level.
421     *
422     * @param b The byte to write.
423     *
424     * @exception java.io.IOException If an I/O error ocurred.
425     * */
426    public final void write(byte b) throws IOException{
427        // As long as pos is less than the length of the buffer we can write
428        // to the buffer. If the position is after the buffer a new buffer is
429        // needed
430        if(pos<byteBuffer.length){
431            if(isReadOnly)
432                throw new IOException("File is read only");
433            byteBuffer[pos]=b;
434            if(pos>=maxByte){
435                maxByte=pos+1;
436            }
437            pos++;
438            byteBufferChanged =true;
439        }
440        else{
441            readNewBuffer(offset+pos);
442            write(b);
443        }
444    }
445
446    /**
447     * Writes aan array of bytes to the stream. Prior to writing, the stream is
448     * realigned at the byte level.
449     *
450     * @param b The array of bytes to write.
451     *
452     * @param offset The first byte in b to write
453     *
454     * @param length The number of bytes from b to write
455     *
456     * @exception java.io.IOException If an I/O error ocurred.
457     * */
458    public final void write(byte[] b, int offset, int length)
459        throws IOException{
460        int i,stop;
461        stop = offset+length;
462        if(stop > b.length)
463            throw new ArrayIndexOutOfBoundsException(b.length);
464        for(i=offset ; i<stop ; i++){
465            write(b[i]);
466        }
467    }
468
469    /**
470     * Writes the byte value of <tt>v</tt> (i.e., 8 least
471     * significant bits) to the output. Prior to writing, the output
472     * should be realigned at the byte level.
473     *
474     * <P>Signed or unsigned data can be written. To write a signed
475     * value just pass the <tt>byte</tt> value as an argument. To
476     * write unsigned data pass the <tt>int</tt> value as an argument
477     * (it will be automatically casted, and only the 8 least
478     * significant bits will be written).
479     *
480     * @param v The value to write to the output
481     *
482     * @exception java.io.IOException If an I/O error ocurred.
483     * */
484    public final void writeByte(int v) throws IOException{
485        write(v);
486    }
487
488    /**
489     * Any data that has been buffered must be written (including
490     * buffering at the bit level), and the stream should be realigned
491     * at the byte level.
492     *
493     * @exception java.io.IOException If an I/O error ocurred.
494     * */
495    public final void flush() throws IOException{
496        if(byteBufferChanged){
497            theFile.seek(offset);
498            theFile.write(byteBuffer,0,maxByte);
499            byteBufferChanged = false;
500        }
501    }
502
503    /**
504     * Reads a signed byte (i.e., 8 bit) from the input. Prior to
505     * reading, the input should be realigned at the byte level.
506     *
507     * @return The next byte-aligned signed byte (8 bit) from the
508     * input.
509     *
510     * @exception java.io.EOFException If the end-of file was reached before
511     * getting all the necessary data.
512     *
513     * @exception java.io.IOException If an I/O error ocurred.
514     * */
515    public final byte readByte() throws EOFException, IOException {
516        if(pos<maxByte){ // The byte can be read from the buffer
517            // In Java, the bytes are always signed.
518            return byteBuffer[pos++];
519        }
520        else if(isEOFInBuffer){ // EOF is reached
521            pos = maxByte+1; // Set position to EOF
522            throw new EOFException();
523        }
524        else { // End of the buffer is reached
525            readNewBuffer(offset+pos);
526            return readByte();
527        }
528    }
529
530    /**
531     * Reads an unsigned byte (i.e., 8 bit) from the input. It is
532     * returned as an <tt>int</tt> since Java does not have an
533     * unsigned byte type. Prior to reading, the input should be
534     * realigned at the byte level.
535     *
536     * @return The next byte-aligned unsigned byte (8 bit) from the
537     * input, as an <tt>int</tt>.
538     *
539     * @exception java.io.EOFException If the end-of file was reached before
540     * getting all the necessary data.
541     *
542     * @exception java.io.IOException If an I/O error ocurred.
543     * */
544    public final int readUnsignedByte() throws EOFException, IOException{
545        return read();
546    }
547
548    /**
549     * Returns the endianess (i.e., byte ordering) of the implementing
550     * class. Note that an implementing class may implement only one
551     * type of endianness or both, which would be decided at creation
552     * time.
553     *
554     * @return Either <tt>EndianType.BIG_ENDIAN</tt> or
555     * <tt>EndianType.LITTLE_ENDIAN</tt>
556     *
557     * @see EndianType
558     * */
559    public int getByteOrdering(){
560        return byteOrdering;
561    }
562
563    /**
564     * Skips <tt>n</tt> bytes from the input. Prior to skipping, the
565     * input should be realigned at the byte level.
566     *
567     * @param n The number of bytes to skip
568     *
569     * @exception java.io.EOFException If the end-of file was reached before
570     * all the bytes could be skipped.
571     *
572     * @exception java.io.IOException If an I/O error ocurred.
573     * */
574    public int skipBytes(int n)throws EOFException, IOException{
575        if(n<0)
576            throw new IllegalArgumentException("Can not skip negative number "+
577                                               "of bytes");
578        if(n <= (maxByte-pos)){
579            pos += n;
580            return n;
581        }
582        else{
583            seek(offset+pos+n);
584            return n;
585        }
586    }
587
588    /**
589     * Returns a string of information about the file
590     * */
591    public String toString(){
592        return "BufferedRandomAccessFile: "+fileName+" ("+
593            ((isReadOnly)?"read only":"read/write")+
594            ")";
595    }
596}