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}