001/* 002 * jDTAUS Core Utilities 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.io.util; 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.io.Serializable; 027import java.util.Arrays; 028import java.util.Locale; 029import org.jdtaus.core.container.ContainerFactory; 030import org.jdtaus.core.io.FileOperations; 031import org.jdtaus.core.lang.spi.MemoryManager; 032import org.jdtaus.core.logging.spi.Logger; 033 034/** 035 * Implementation of elementary I/O operations in heap memory. 036 * <p>This implementation performs I/O in memory. The value of property 037 * {@code length} is limited to a maximum of {@code Integer.MAX_VALUE} (4 GB). 038 * </p> 039 * 040 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 041 * @version $JDTAUS: MemoryFileOperations.java 8644 2012-09-27 06:46:11Z schulte $ 042 */ 043public final class MemoryFileOperations 044 implements FileOperations, Serializable, Cloneable 045{ 046 //--Fields------------------------------------------------------------------ 047 048 /** 049 * Data to operate on. 050 * @serial 051 */ 052 private byte[] data; 053 054 /** 055 * FilePointer. 056 * @serial 057 */ 058 private long filePointer; 059 060 /** 061 * Actual length. 062 * @serial 063 */ 064 private int length; 065 066 /** 067 * Default temporary buffer. 068 * @serial 069 */ 070 private byte[] defaultBuffer; 071 072 //------------------------------------------------------------------Fields-- 073 //--Properties-------------------------------------------------------------- 074 075// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties 076 // This section is managed by jdtaus-container-mojo. 077 078 /** 079 * Gets the value of property <code>streamBufferSize</code>. 080 * 081 * @return Size of the buffer for buffering streams. 082 */ 083 private int getStreamBufferSize() 084 { 085 return ( (java.lang.Integer) ContainerFactory.getContainer(). 086 getProperty( this, "streamBufferSize" ) ).intValue(); 087 088 } 089 090// </editor-fold>//GEN-END:jdtausProperties 091 092 //--------------------------------------------------------------Properties-- 093 //--Dependencies------------------------------------------------------------ 094 095// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies 096 // This section is managed by jdtaus-container-mojo. 097 098 /** 099 * Gets the configured <code>Logger</code> implementation. 100 * 101 * @return The configured <code>Logger</code> implementation. 102 */ 103 private Logger getLogger() 104 { 105 return (Logger) ContainerFactory.getContainer(). 106 getDependency( this, "Logger" ); 107 108 } 109 110 /** 111 * Gets the configured <code>MemoryManager</code> implementation. 112 * 113 * @return The configured <code>MemoryManager</code> implementation. 114 */ 115 private MemoryManager getMemoryManager() 116 { 117 return (MemoryManager) ContainerFactory.getContainer(). 118 getDependency( this, "MemoryManager" ); 119 120 } 121 122 /** 123 * Gets the configured <code>Locale</code> implementation. 124 * 125 * @return The configured <code>Locale</code> implementation. 126 */ 127 private Locale getLocale() 128 { 129 return (Locale) ContainerFactory.getContainer(). 130 getDependency( this, "Locale" ); 131 132 } 133 134// </editor-fold>//GEN-END:jdtausDependencies 135 136 //------------------------------------------------------------Dependencies-- 137 //--FileOperations---------------------------------------------------------- 138 139 public long getLength() 140 { 141 return this.length; 142 } 143 144 /** 145 * {@inheritDoc} 146 * 147 * @throws IllegalArgumentException if {@code newLength} is negative or 148 * greater than {@code Integer.MAX_VALUE}. 149 */ 150 public void setLength( final long newLength ) 151 { 152 if ( newLength < 0L || newLength > Integer.MAX_VALUE ) 153 { 154 throw new IllegalArgumentException( Long.toString( newLength ) ); 155 } 156 157 this.ensureCapacity( (int) newLength ); 158 this.length = (int) newLength; 159 if ( this.filePointer > this.length ) 160 { 161 this.filePointer = this.length; 162 } 163 } 164 165 public long getFilePointer() 166 { 167 return this.filePointer; 168 } 169 170 public void setFilePointer( final long pos ) 171 { 172 // Preconditions. 173 if ( pos < 0L || pos > Integer.MAX_VALUE ) 174 { 175 throw new IllegalArgumentException( Long.toString( pos ) ); 176 } 177 178 this.filePointer = pos; 179 } 180 181 public int read( final byte[] buf, final int off, final int len ) 182 { 183 final int ret; 184 185 // Preconditions. 186 if ( buf == null ) 187 { 188 throw new NullPointerException( "buf" ); 189 } 190 if ( off < 0 ) 191 { 192 throw new ArrayIndexOutOfBoundsException( off ); 193 } 194 if ( len < 0 ) 195 { 196 throw new ArrayIndexOutOfBoundsException( len ); 197 } 198 if ( off + len > buf.length ) 199 { 200 throw new ArrayIndexOutOfBoundsException( off + len ); 201 } 202 if ( this.filePointer + len > Integer.MAX_VALUE ) 203 { 204 throw new ArrayIndexOutOfBoundsException( Integer.MAX_VALUE ); 205 } 206 207 if ( len == 0 ) 208 { 209 ret = 0; 210 } 211 else if ( this.filePointer >= this.length ) 212 { 213 // EOF 214 ret = FileOperations.EOF; 215 } 216 else if ( this.filePointer + len > this.length ) 217 { 218 // less than len byte before EOF 219 final int remaining = (int) ( this.length - this.filePointer ); 220 System.arraycopy( this.data, (int) this.filePointer, buf, off, 221 remaining ); 222 223 this.filePointer += remaining; 224 ret = remaining; 225 } 226 else 227 { 228 // copy len byte into buf. 229 System.arraycopy( 230 this.data, (int) this.filePointer, buf, off, len ); 231 232 this.filePointer += len; 233 ret = len; 234 } 235 236 return ret; 237 } 238 239 public void write( final byte[] buf, final int off, final int len ) 240 { 241 // Preconditions. 242 if ( buf == null ) 243 { 244 throw new NullPointerException( "buf" ); 245 } 246 if ( off < 0 ) 247 { 248 throw new ArrayIndexOutOfBoundsException( off ); 249 } 250 if ( len < 0 ) 251 { 252 throw new ArrayIndexOutOfBoundsException( len ); 253 } 254 if ( off + len > buf.length ) 255 { 256 throw new ArrayIndexOutOfBoundsException( off + len ); 257 } 258 259 final long newLen = this.filePointer + len; 260 if ( newLen > Integer.MAX_VALUE ) 261 { 262 throw new ArrayIndexOutOfBoundsException( Integer.MAX_VALUE ); 263 } 264 265 if ( newLen > this.length ) 266 { 267 this.setLength( newLen ); 268 } 269 270 System.arraycopy( buf, off, this.data, (int) this.filePointer, len ); 271 this.filePointer += len; 272 } 273 274 public void read( final OutputStream out ) throws IOException 275 { 276 if ( out == null ) 277 { 278 throw new NullPointerException( "out" ); 279 } 280 281 out.write( this.data, 0, this.length ); 282 this.filePointer = this.length; 283 } 284 285 public void write( final InputStream in ) throws IOException 286 { 287 if ( in == null ) 288 { 289 throw new NullPointerException( "in" ); 290 } 291 292 int read; 293 final byte[] buf = this.getStreamBuffer(); 294 295 while ( ( read = in.read( buf, 0, buf.length ) ) != FileOperations.EOF ) 296 { 297 this.write( buf, 0, read ); 298 } 299 } 300 301 /** 302 * {@inheritDoc} 303 * <p>Since this implementation does not use any system resources to 304 * release, this method does nothing. In contrast to other 305 * {@code FileOperations} implementations the instance can still be used 306 * after calling this method. It would be a mistake to write an application 307 * which relies on this behaviour, however.</p> 308 */ 309 public void close() 310 { 311 } 312 313 //----------------------------------------------------------FileOperations-- 314 //--MemoryFileOperations---------------------------------------------------- 315 316 /** Creates a new {@code MemoryFileOperations} instance of no length. */ 317 public MemoryFileOperations() 318 { 319 this.filePointer = 0L; 320 this.data = new byte[ 0 ]; 321 this.length = 0; 322 } 323 324 /** 325 * Creates a new {@code MemoryFileOperations} instance of no length 326 * taking an initial capacity. 327 * 328 * @param initialCapacity the number of bytes to pre-allocate when 329 * creating the new instance. 330 * 331 * @throws OutOfMemoryError if not enough memory is available to create a 332 * buffer with a length of {@code initialCapacity}. 333 * 334 * @see #ensureCapacity(int) 335 */ 336 public MemoryFileOperations( final int initialCapacity ) 337 { 338 this.filePointer = 0L; 339 this.length = 0; 340 this.data = this.getMemoryManager().allocateBytes( initialCapacity ); 341 } 342 343 /** 344 * Creates a new {@code MemoryFileOperations} instance holding {@code buf}. 345 * 346 * @param buf bytes to initialize the instance with. 347 * 348 * @throws NullPointerException if {@code buf} is {@code null}. 349 */ 350 public MemoryFileOperations( final byte[] buf ) 351 { 352 this(); 353 354 if ( buf == null ) 355 { 356 throw new NullPointerException( "buf" ); 357 } 358 359 this.data = buf; 360 this.length = buf.length; 361 this.filePointer = 0L; 362 } 363 364 /** 365 * Gets the capacity of the instance. 366 * 367 * @return the capacity of the instance in byte. 368 */ 369 public int getCapacity() 370 { 371 return this.data.length; 372 } 373 374 /** 375 * Increases the capacity of the instance, if necessary, to ensure that it 376 * can hold at least the number of bytes specified by the minimum capacity 377 * argument. 378 * 379 * @param minimumCapacity the minimum capacity to ensure. 380 */ 381 public void ensureCapacity( final int minimumCapacity ) 382 { 383 final int oldLength = this.data.length; 384 385 while ( this.data.length < minimumCapacity ) 386 { 387 final byte[] newData = this.getMemoryManager().allocateBytes( 388 this.data.length * 2 >= minimumCapacity 389 ? this.data.length * 2 390 : minimumCapacity ); 391 392 Arrays.fill( newData, (byte) 0 ); 393 System.arraycopy( this.data, 0, newData, 0, this.data.length ); 394 this.data = newData; 395 } 396 397 if ( oldLength != this.data.length && 398 this.getLogger().isDebugEnabled() ) 399 { 400 this.getLogger().debug( 401 this.getLogResizeMessage( this.getLocale(), 402 new Long( this.data.length ) ) ); 403 404 } 405 } 406 407 /** 408 * Gets the file contents. 409 * 410 * @return the file contents of the instance. 411 * 412 * @throws OutOfMemoryError if not enough memory is available. 413 */ 414 public byte[] getData() 415 { 416 final int len = (int) this.getLength(); 417 final byte[] ret = this.getMemoryManager().allocateBytes( len ); 418 419 System.arraycopy( this.data, 0, ret, 0, len ); 420 421 return ret; 422 } 423 424 /** 425 * Gets a buffer for buffering streams. 426 * 427 * @return a buffer for buffering streams. 428 */ 429 private byte[] getStreamBuffer() 430 { 431 if ( this.defaultBuffer == null ) 432 { 433 this.defaultBuffer = this.getMemoryManager(). 434 allocateBytes( this.getStreamBufferSize() < 0 435 ? 0 436 : this.getStreamBufferSize() ); 437 438 } 439 440 return this.defaultBuffer; 441 } 442 443 //----------------------------------------------------MemoryFileOperations-- 444 //--Object------------------------------------------------------------------ 445 446 /** 447 * Indicates whether some other object is equal to this one by comparing 448 * the contents from memory. 449 * 450 * @param o the reference object with which to compare. 451 * 452 * @return {@code true} if this object is the same as {@code o}; 453 * {@code false} otherwise. 454 */ 455 public boolean equals( final Object o ) 456 { 457 boolean equal = this == o; 458 459 if ( !equal && o instanceof MemoryFileOperations ) 460 { 461 final MemoryFileOperations that = (MemoryFileOperations) o; 462 equal = Arrays.equals( getData(), that.getData() ); 463 } 464 465 return equal; 466 } 467 468 /** 469 * Returns a hash code value for this object. 470 * 471 * @return a hash code value for this object. 472 */ 473 public int hashCode() 474 { 475 // JDK: As of JDK 1.5, Arrays.hashCode( getData() ). 476 final byte[] bytes = getData(); 477 478 int result = 1; 479 for ( int i = 0, s0 = bytes.length; i < s0; i++ ) 480 { 481 result = 31 * result + bytes[i]; 482 } 483 484 return result; 485 } 486 487 /** 488 * Creates and returns a deep copy of this object. 489 * 490 * @return a clone of this instance. 491 */ 492 public Object clone() 493 { 494 try 495 { 496 return super.clone(); 497 } 498 catch ( CloneNotSupportedException e ) 499 { 500 throw new AssertionError( e ); 501 } 502 } 503 504 //------------------------------------------------------------------Object-- 505 //--Messages---------------------------------------------------------------- 506 507// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages 508 // This section is managed by jdtaus-container-mojo. 509 510 /** 511 * Gets the text of message <code>logResize</code>. 512 * <blockquote><pre>aktuelle Puffergröße: {0, number} Byte</pre></blockquote> 513 * <blockquote><pre>current buffer size: {0, number} byte</pre></blockquote> 514 * 515 * @param locale The locale of the message instance to return. 516 * @param bufferSize The current size of the internal buffer. 517 * 518 * @return Information about the size of the internal buffer. 519 */ 520 private String getLogResizeMessage( final Locale locale, 521 final java.lang.Number bufferSize ) 522 { 523 return ContainerFactory.getContainer(). 524 getMessage( this, "logResize", locale, 525 new Object[] 526 { 527 bufferSize 528 }); 529 530 } 531 532// </editor-fold>//GEN-END:jdtausMessages 533 534 //----------------------------------------------------------------Messages-- 535}