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.RandomAccessFile; 027import java.util.Locale; 028import org.jdtaus.core.container.ContainerFactory; 029import org.jdtaus.core.io.FileOperations; 030import org.jdtaus.core.lang.spi.MemoryManager; 031 032/** 033 * Adapts a {@link java.io.RandomAccessFile} to {@code FileOperations}. 034 * 035 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 036 * @version $JDTAUS: RandomAccessFileOperations.java 8743 2012-10-07 03:06:20Z schulte $ 037 */ 038public final class RandomAccessFileOperations implements FileOperations 039{ 040 //--Dependencies------------------------------------------------------------ 041 042// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies 043 // This section is managed by jdtaus-container-mojo. 044 045 /** 046 * Gets the configured <code>MemoryManager</code> implementation. 047 * 048 * @return The configured <code>MemoryManager</code> implementation. 049 */ 050 private MemoryManager getMemoryManager() 051 { 052 return (MemoryManager) ContainerFactory.getContainer(). 053 getDependency( this, "MemoryManager" ); 054 055 } 056 057 /** 058 * Gets the configured <code>Locale</code> implementation. 059 * 060 * @return The configured <code>Locale</code> implementation. 061 */ 062 private Locale getLocale() 063 { 064 return (Locale) ContainerFactory.getContainer(). 065 getDependency( this, "Locale" ); 066 067 } 068 069// </editor-fold>//GEN-END:jdtausDependencies 070 071 //------------------------------------------------------------Dependencies-- 072 //--Properties-------------------------------------------------------------- 073 074// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties 075 // This section is managed by jdtaus-container-mojo. 076 077 /** 078 * Gets the value of property <code>streamBufferSize</code>. 079 * 080 * @return Size of the buffer for buffering streams. 081 */ 082 private int getStreamBufferSize() 083 { 084 return ( (java.lang.Integer) ContainerFactory.getContainer(). 085 getProperty( this, "streamBufferSize" ) ).intValue(); 086 087 } 088 089// </editor-fold>//GEN-END:jdtausProperties 090 091 //--------------------------------------------------------------Properties-- 092 //--FileOperations---------------------------------------------------------- 093 094 /** Cache for the value of property {@code length}. */ 095 private transient long cachedLength = NO_CACHEDLENGTH; 096 097 private static final long NO_CACHEDLENGTH = Long.MIN_VALUE; 098 099 public long getLength() throws IOException 100 { 101 this.assertNotClosed(); 102 103 return this.cachedLength != NO_CACHEDLENGTH 104 ? this.cachedLength 105 : ( this.cachedLength = 106 this.getRandomAccessFile().length() ); 107 108 } 109 110 public void setLength( final long newLength ) throws IOException 111 { 112 if ( newLength < 0L ) 113 { 114 throw new IllegalArgumentException( Long.toString( newLength ) ); 115 } 116 117 this.assertNotClosed(); 118 119 this.getRandomAccessFile().setLength( newLength ); 120 this.cachedLength = newLength; 121 } 122 123 public long getFilePointer() throws IOException 124 { 125 this.assertNotClosed(); 126 127 return this.getRandomAccessFile().getFilePointer(); 128 } 129 130 public void setFilePointer( final long pos ) throws IOException 131 { 132 this.assertNotClosed(); 133 134 this.getRandomAccessFile().seek( pos ); 135 } 136 137 public void write( final byte[] buf, final int off, final int len ) 138 throws IOException 139 { 140 this.assertNotClosed(); 141 142 final RandomAccessFile file = this.getRandomAccessFile(); 143 final long pointer = file.getFilePointer(); 144 145 file.write( buf, off, len ); 146 147 if ( this.cachedLength != NO_CACHEDLENGTH && 148 pointer + len > this.cachedLength ) 149 { 150 this.cachedLength = file.length(); 151 } 152 } 153 154 public int read( final byte[] buf, final int off, final int len ) 155 throws IOException 156 { 157 this.assertNotClosed(); 158 159 return this.getRandomAccessFile().read( buf, off, len ); 160 } 161 162 public void read( final OutputStream out ) throws IOException 163 { 164 if ( out == null ) 165 { 166 throw new NullPointerException( "out" ); 167 } 168 169 this.assertNotClosed(); 170 171 int read; 172 long toRead = this.getLength(); 173 final byte[] buf = this.getStreamBuffer(); 174 175 if ( toRead > 0L ) 176 { 177 this.setFilePointer( 0L ); 178 do 179 { 180 read = this.read( buf, 0, buf.length ); 181 182 assert read != FileOperations.EOF : "Unexpected end of file."; 183 184 toRead -= read; 185 out.write( buf, 0, read ); 186 } 187 while ( toRead > 0L ); 188 } 189 } 190 191 public void write( final InputStream in ) throws IOException 192 { 193 if ( in == null ) 194 { 195 throw new NullPointerException( "in" ); 196 } 197 198 this.assertNotClosed(); 199 200 int read; 201 final byte[] buf = this.getStreamBuffer(); 202 203 while ( ( read = in.read( buf, 0, buf.length ) ) != FileOperations.EOF ) 204 { 205 this.write( buf, 0, read ); 206 } 207 } 208 209 /** 210 * {@inheritDoc} 211 * Closes the {@code RandomAccessFile} backing the instance. 212 * 213 * @throws IOException if closing the {@code RandomAccessFile} backing the 214 * instance fails. 215 */ 216 public void close() throws IOException 217 { 218 this.assertNotClosed(); 219 220 this.getRandomAccessFile().close(); 221 this.closed = true; 222 } 223 224 //----------------------------------------------------------FileOperations-- 225 //--RandomAccessFileOperations---------------------------------------------- 226 227 /** Stream buffer. */ 228 private byte[] streamBuffer; 229 230 /** Flags the instance as beeing closed. */ 231 private boolean closed; 232 233 /** {@code RandomAccessFile} requirement. */ 234 private RandomAccessFile randomAccessFile; 235 236 /** 237 * Creates a new {@code RandomAccessFileOperations} instance adapting 238 * {@code file}. 239 * 240 * @param file an {@code RandomAccessFile} instance to use as a 241 * {@code FileOperations} implementation. 242 * 243 * @throws NullPointerException if {@code file} is {@code null}. 244 */ 245 public RandomAccessFileOperations( final RandomAccessFile file ) 246 { 247 super(); 248 249 if ( file == null ) 250 { 251 throw new NullPointerException( "file" ); 252 } 253 254 this.randomAccessFile = file; 255 } 256 257 /** 258 * Gets the random access file operations are performed with. 259 * 260 * @return the {@code RandomAccessFile} instance operations are performed 261 * with or {@code null}. 262 */ 263 public RandomAccessFile getRandomAccessFile() 264 { 265 return this.randomAccessFile; 266 } 267 268 /** 269 * Checks that the instance is not closed. 270 * 271 * @throws IOException if the instance is closed. 272 */ 273 private void assertNotClosed() throws IOException 274 { 275 if ( this.closed ) 276 { 277 throw new IOException( this.getAlreadyClosedMessage( 278 this.getLocale() ) ); 279 280 } 281 } 282 283 /** 284 * Gets a buffer for buffering streams. 285 * 286 * @return a buffer for buffering streams. 287 */ 288 private byte[] getStreamBuffer() 289 { 290 if ( this.streamBuffer == null ) 291 { 292 this.streamBuffer = this.getMemoryManager(). 293 allocateBytes( this.getStreamBufferSize() < 0 294 ? 0 295 : this.getStreamBufferSize() ); 296 297 } 298 299 return this.streamBuffer; 300 } 301 302 //----------------------------------------------RandomAccessFileOperations-- 303 //--Messages---------------------------------------------------------------- 304 305// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages 306 // This section is managed by jdtaus-container-mojo. 307 308 /** 309 * Gets the text of message <code>alreadyClosed</code>. 310 * <blockquote><pre>Instanz geschlossen - keine E/A-Operationen möglich.</pre></blockquote> 311 * <blockquote><pre>Instance closed - cannot perform I/O.</pre></blockquote> 312 * 313 * @param locale The locale of the message instance to return. 314 * 315 * @return Message stating that an instance is already closed. 316 */ 317 private String getAlreadyClosedMessage( final Locale locale ) 318 { 319 return ContainerFactory.getContainer(). 320 getMessage( this, "alreadyClosed", locale, null ); 321 322 } 323 324// </editor-fold>//GEN-END:jdtausMessages 325 326 //----------------------------------------------------------------Messages-- 327}