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