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:
045: package jj2000.j2k.io;
046:
047: import 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: * */
069: public abstract class BufferedRandomAccessFile implements
070: 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, String mode,
139: int bufferSize) throws IOException {
140:
141: fileName = file.getName();
142: if (mode.equals("rw") || mode.equals("rw+")) { // mode read / write
143: isReadOnly = false;
144: if (mode.equals("rw")) { // mode read / (over)write
145: if (file.exists()) // Output file already exists
146: file.delete();
147: }
148: mode = "rw";
149: }
150: theFile = new RandomAccessFile(file, mode);
151: byteBuffer = new byte[bufferSize];
152: readNewBuffer(0);
153: }
154:
155: /**
156: * Constructor. Uses the default value for the byte-buffer
157: * size (512 bytes).
158: *
159: * @param file The file associated with the buffer
160: *
161: * @param mode "r" for read, "rw" or "rw+" for read and write mode
162: * ("rw+" opens the file for update whereas "rw" removes
163: * it before. So the 2 modes are different only if the
164: * file already exists).
165: *
166: * @exception java.io.IOException If an I/O error ocurred.
167: * */
168: protected BufferedRandomAccessFile(File file, String mode)
169: throws IOException {
170:
171: this (file, mode, 512);
172: }
173:
174: /**
175: * Constructor. Always needs a size for the buffer.
176: *
177: * @param name The name of the file associated with the buffer
178: *
179: * @param mode "r" for read, "rw" or "rw+" for read and write mode
180: * ("rw+" opens the file for update whereas "rw" removes
181: * it before. So the 2 modes are different only if the
182: * file already exists).
183: *
184: * @param bufferSize The number of bytes to buffer
185: *
186: * @exception java.io.IOException If an I/O error ocurred.
187: * */
188: protected BufferedRandomAccessFile(String name, String mode,
189: int bufferSize) throws IOException {
190: this (new File(name), mode, bufferSize);
191: }
192:
193: /**
194: * Constructor. Uses the default value for the byte-buffer
195: * size (512 bytes).
196: *
197: * @param name The name of the file associated with the buffer
198: *
199: * @param mode "r" for read, "rw" or "rw+" for read and write mode
200: * ("rw+" opens the file for update whereas "rw" removes
201: * it before. So the 2 modes are different only if the
202: * file already exists).
203: *
204: * @exception java.io.IOException If an I/O error ocurred.
205: * */
206: protected BufferedRandomAccessFile(String name, String mode)
207: throws IOException {
208:
209: this (name, mode, 512);
210: }
211:
212: /**
213: * Reads a new buffer from the file. If there has been any
214: * changes made since the buffer was read, the buffer is
215: * first written to the file.
216: *
217: * @param off The offset where to move to.
218: *
219: * @exception java.io.IOException If an I/O error ocurred.
220: * */
221: protected final void readNewBuffer(int off) throws IOException {
222:
223: /* If the buffer have changed. We need to write it to
224: * the file before reading a new buffer.
225: */
226: if (byteBufferChanged) {
227: flush();
228: }
229: // Don't allow to seek beyond end of file if reading only
230: if (isReadOnly && off >= theFile.length()) {
231: throw new EOFException();
232: }
233: // Set new offset
234: offset = off;
235:
236: theFile.seek(offset);
237:
238: maxByte = theFile.read(byteBuffer, 0, byteBuffer.length);
239: pos = 0;
240:
241: if (maxByte < byteBuffer.length) { // Not enough data in input file.
242: isEOFInBuffer = true;
243: if (maxByte == -1) {
244: maxByte++;
245: }
246: } else {
247: isEOFInBuffer = false;
248: }
249: }
250:
251: /**
252: * Closes the buffered random access file
253: *
254: * @exception java.io.IOException If an I/O error ocurred.
255: * */
256: public void close() throws IOException {
257: /* If the buffer has been changed, it need to be saved before
258: * closing
259: */
260: flush();
261: byteBuffer = null; // Release the byte-buffer reference
262: theFile.close();
263: }
264:
265: /**
266: * Returns the current offset in the file
267: * */
268: public int getPos() {
269: return (offset + pos);
270: }
271:
272: /**
273: * Returns the current length of the stream, in bytes, taking into
274: * account any buffering.
275: *
276: * @return The length of the stream, in bytes.
277: *
278: * @exception java.io.IOException If an I/O error ocurred.
279: * */
280: public int length() throws IOException {
281: int len;
282:
283: len = (int) theFile.length();
284:
285: // If the position in the buffer is not past the end of the file,
286: // the length of theFile is the length of the stream
287: if ((offset + maxByte) <= len) {
288: return (len);
289: } else { // If not, the file is extended due to the buffering
290: return (offset + maxByte);
291: }
292: }
293:
294: /**
295: * Moves the current position to the given offset at which the
296: * next read or write occurs. The offset is measured from the
297: * beginning of the stream.
298: *
299: * @param off The offset where to move to.
300: *
301: * @exception EOFException If in read-only and seeking beyond EOF.
302: *
303: * @exception java.io.IOException If an I/O error ocurred.
304: * */
305: public void seek(int off) throws IOException {
306: /* If the new offset is within the buffer, only the pos value needs
307: * to be modified. Else, the buffer must be moved. */
308: if ((off >= offset) && (off < (offset + byteBuffer.length))) {
309: if (isReadOnly && isEOFInBuffer && off > offset + maxByte) {
310: // We are seeking beyond EOF in read-only mode!
311: throw new EOFException();
312: }
313: pos = off - offset;
314: } else {
315: readNewBuffer(off);
316: }
317: }
318:
319: /**
320: * Reads an unsigned byte of data from the stream. Prior to reading, the
321: * stream is realigned at the byte level.
322: *
323: * @return The byte read.
324: *
325: * @exception java.io.IOException If an I/O error ocurred.
326: *
327: * @exception java.io.EOFException If the end of file was reached
328: * */
329: public final int read() throws IOException, EOFException {
330: if (pos < maxByte) { // The byte can be read from the buffer
331: // In Java, the bytes are always signed.
332: return (byteBuffer[pos++] & 0xFF);
333: } else if (isEOFInBuffer) { // EOF is reached
334: pos = maxByte + 1; // Set position to EOF
335: throw new EOFException();
336: } else { // End of the buffer is reached
337: readNewBuffer(offset + pos);
338: return read();
339: }
340: }
341:
342: /**
343: * Reads up to len bytes of data from this file into an array of
344: * bytes. This method reads repeatedly from the stream until all the bytes
345: * are read. This method blocks until all the bytes are read, the end of
346: * the stream is detected, or an exception is thrown.
347: *
348: * @param b The buffer into which the data is to be read. It must be long
349: * enough.
350: *
351: * @param off The index in 'b' where to place the first byte read.
352: *
353: * @param len The number of bytes to read.
354: *
355: * @exception EOFException If the end-of file was reached before
356: * getting all the necessary data.
357: *
358: * @exception IOException If an I/O error ocurred.
359: * */
360: public final void readFully(byte b[], int off, int len)
361: throws IOException {
362: int clen; // current length to read
363: while (len > 0) {
364: // There still is some data to read
365: if (pos < maxByte) { // We can read some data from buffer
366: clen = maxByte - pos;
367: if (clen > len)
368: clen = len;
369: System.arraycopy(byteBuffer, pos, b, off, clen);
370: pos += clen;
371: off += clen;
372: len -= clen;
373: } else if (isEOFInBuffer) {
374: pos = maxByte + 1; // Set position to EOF
375: throw new EOFException();
376: } else { // Buffer empty => get more data
377: readNewBuffer(offset + pos);
378: }
379: }
380: }
381:
382: /**
383: * Writes a byte to the stream. Prior to writing, the stream is
384: * realigned at the byte level.
385: *
386: * @param b The byte to write. The lower 8 bits of <tt>b</tt> are
387: * written.
388: *
389: * @exception java.io.IOException If an I/O error ocurred.
390: * */
391: public final void write(int b) throws IOException {
392: // As long as pos is less than the length of the buffer we can write
393: // to the buffer. If the position is after the buffer a new buffer is
394: // needed
395: if (pos < byteBuffer.length) {
396: if (isReadOnly)
397: throw new IOException("File is read only");
398: byteBuffer[pos] = (byte) b;
399: if (pos >= maxByte) {
400: maxByte = pos + 1;
401: }
402: pos++;
403: byteBufferChanged = true;
404: } else {
405: readNewBuffer(offset + pos);
406: write(b);
407: }
408: }
409:
410: /**
411: * Writes a byte to the stream. Prior to writing, the stream is
412: * realigned at the byte level.
413: *
414: * @param b The byte to write.
415: *
416: * @exception java.io.IOException If an I/O error ocurred.
417: * */
418: public final void write(byte b) throws IOException {
419: // As long as pos is less than the length of the buffer we can write
420: // to the buffer. If the position is after the buffer a new buffer is
421: // needed
422: if (pos < byteBuffer.length) {
423: if (isReadOnly)
424: throw new IOException("File is read only");
425: byteBuffer[pos] = b;
426: if (pos >= maxByte) {
427: maxByte = pos + 1;
428: }
429: pos++;
430: byteBufferChanged = true;
431: } else {
432: readNewBuffer(offset + pos);
433: write(b);
434: }
435: }
436:
437: /**
438: * Writes aan array of bytes to the stream. Prior to writing, the stream is
439: * realigned at the byte level.
440: *
441: * @param b The array of bytes to write.
442: *
443: * @param offset The first byte in b to write
444: *
445: * @param length The number of bytes from b to write
446: *
447: * @exception java.io.IOException If an I/O error ocurred.
448: * */
449: public final void write(byte[] b, int offset, int length)
450: throws IOException {
451: int i, stop;
452: stop = offset + length;
453: if (stop > b.length)
454: throw new ArrayIndexOutOfBoundsException(b.length);
455: for (i = offset; i < stop; i++) {
456: write(b[i]);
457: }
458: }
459:
460: /**
461: * Writes the byte value of <tt>v</tt> (i.e., 8 least
462: * significant bits) to the output. Prior to writing, the output
463: * should be realigned at the byte level.
464: *
465: * <P>Signed or unsigned data can be written. To write a signed
466: * value just pass the <tt>byte</tt> value as an argument. To
467: * write unsigned data pass the <tt>int</tt> value as an argument
468: * (it will be automatically casted, and only the 8 least
469: * significant bits will be written).
470: *
471: * @param v The value to write to the output
472: *
473: * @exception java.io.IOException If an I/O error ocurred.
474: * */
475: public final void writeByte(int v) throws IOException {
476: write(v);
477: }
478:
479: /**
480: * Any data that has been buffered must be written (including
481: * buffering at the bit level), and the stream should be realigned
482: * at the byte level.
483: *
484: * @exception java.io.IOException If an I/O error ocurred.
485: * */
486: public final void flush() throws IOException {
487: if (byteBufferChanged) {
488: theFile.seek(offset);
489: theFile.write(byteBuffer, 0, maxByte);
490: byteBufferChanged = false;
491: }
492: }
493:
494: /**
495: * Reads a signed byte (i.e., 8 bit) from the input. Prior to
496: * reading, the input should be realigned at the byte level.
497: *
498: * @return The next byte-aligned signed byte (8 bit) from the
499: * input.
500: *
501: * @exception java.io.EOFException If the end-of file was reached before
502: * getting all the necessary data.
503: *
504: * @exception java.io.IOException If an I/O error ocurred.
505: * */
506: public final byte readByte() throws EOFException, IOException {
507: if (pos < maxByte) { // The byte can be read from the buffer
508: // In Java, the bytes are always signed.
509: return byteBuffer[pos++];
510: } else if (isEOFInBuffer) { // EOF is reached
511: pos = maxByte + 1; // Set position to EOF
512: throw new EOFException();
513: } else { // End of the buffer is reached
514: readNewBuffer(offset + pos);
515: return readByte();
516: }
517: }
518:
519: /**
520: * Reads an unsigned byte (i.e., 8 bit) from the input. It is
521: * returned as an <tt>int</tt> since Java does not have an
522: * unsigned byte type. Prior to reading, the input should be
523: * realigned at the byte level.
524: *
525: * @return The next byte-aligned unsigned byte (8 bit) from the
526: * input, as an <tt>int</tt>.
527: *
528: * @exception java.io.EOFException If the end-of file was reached before
529: * getting all the necessary data.
530: *
531: * @exception java.io.IOException If an I/O error ocurred.
532: * */
533: public final int readUnsignedByte() throws EOFException,
534: IOException {
535: return read();
536: }
537:
538: /**
539: * Returns the endianess (i.e., byte ordering) of the implementing
540: * class. Note that an implementing class may implement only one
541: * type of endianness or both, which would be decided at creation
542: * time.
543: *
544: * @return Either <tt>EndianType.BIG_ENDIAN</tt> or
545: * <tt>EndianType.LITTLE_ENDIAN</tt>
546: *
547: * @see EndianType
548: * */
549: public int getByteOrdering() {
550: return byteOrdering;
551: }
552:
553: /**
554: * Skips <tt>n</tt> bytes from the input. Prior to skipping, the
555: * input should be realigned at the byte level.
556: *
557: * @param n The number of bytes to skip
558: *
559: * @exception java.io.EOFException If the end-of file was reached before
560: * all the bytes could be skipped.
561: *
562: * @exception java.io.IOException If an I/O error ocurred.
563: * */
564: public int skipBytes(int n) throws EOFException, IOException {
565: if (n < 0)
566: throw new IllegalArgumentException(
567: "Can not skip negative number " + "of bytes");
568: if (n <= (maxByte - pos)) {
569: pos += n;
570: return n;
571: } else {
572: seek(offset + pos + n);
573: return n;
574: }
575: }
576:
577: /**
578: * Returns a string of information about the file
579: * */
580: public String toString() {
581: return "BufferedRandomAccessFile: " + fileName + " ("
582: + ((isReadOnly) ? "read only" : "read/write") + ")";
583: }
584: }
|