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 8641 2012-09-27 06:45:17Z 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( 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( long pos ) throws IOException
131    {
132        this.assertNotClosed();
133
134        this.getRandomAccessFile().seek( pos );
135    }
136
137    public void write( byte[] buf, int off, int len ) throws IOException
138    {
139        this.assertNotClosed();
140
141        final RandomAccessFile file = this.getRandomAccessFile();
142        final long pointer = file.getFilePointer();
143
144        file.write( buf, off, len );
145
146        if ( this.cachedLength != NO_CACHEDLENGTH &&
147             pointer + len > this.cachedLength )
148        {
149            this.cachedLength = file.length();
150        }
151    }
152
153    public int read( byte[] buf, int off, int len ) throws IOException
154    {
155        this.assertNotClosed();
156
157        return this.getRandomAccessFile().read( buf, off, len );
158    }
159
160    public void read( final OutputStream out ) throws IOException
161    {
162        if ( out == null )
163        {
164            throw new NullPointerException( "out" );
165        }
166
167        this.assertNotClosed();
168
169        int read;
170        long toRead = this.getLength();
171        final byte[] buf = this.getStreamBuffer();
172
173        if ( toRead > 0L )
174        {
175            this.setFilePointer( 0L );
176            do
177            {
178                read = this.read( buf, 0, buf.length );
179
180                assert read != FileOperations.EOF : "Unexpected end of file.";
181
182                toRead -= read;
183                out.write( buf, 0, read );
184            }
185            while ( toRead > 0L );
186        }
187    }
188
189    public void write( final InputStream in ) throws IOException
190    {
191        if ( in == null )
192        {
193            throw new NullPointerException( "in" );
194        }
195
196        this.assertNotClosed();
197
198        int read;
199        final byte[] buf = this.getStreamBuffer();
200
201        while ( ( read = in.read( buf, 0, buf.length ) ) != FileOperations.EOF )
202        {
203            this.write( buf, 0, read );
204        }
205    }
206
207    /**
208     * {@inheritDoc}
209     * Closes the {@code RandomAccessFile} backing the instance.
210     *
211     * @throws IOException if closing the {@code RandomAccessFile} backing the
212     * instance fails.
213     */
214    public void close() throws IOException
215    {
216        this.assertNotClosed();
217
218        this.getRandomAccessFile().close();
219        this.closed = true;
220    }
221
222    //----------------------------------------------------------FileOperations--
223    //--RandomAccessFileOperations----------------------------------------------
224
225    /** Stream buffer. */
226    private byte[] streamBuffer;
227
228    /** Flags the instance as beeing closed. */
229    private boolean closed;
230
231    /** {@code RandomAccessFile} requirement. */
232    private RandomAccessFile randomAccessFile;
233
234    /**
235     * Creates a new {@code RandomAccessFileOperations} instance adapting
236     * {@code file}.
237     *
238     * @param file an {@code RandomAccessFile} instance to use as a
239     * {@code FileOperations} implementation.
240     *
241     * @throws NullPointerException if {@code file} is {@code null}.
242     */
243    public RandomAccessFileOperations( final RandomAccessFile file )
244    {
245        super();
246
247        if ( file == null )
248        {
249            throw new NullPointerException( "file" );
250        }
251
252        this.randomAccessFile = file;
253    }
254
255    /**
256     * Gets the random access file operations are performed with.
257     *
258     * @return the {@code RandomAccessFile} instance operations are performed
259     * with or {@code null}.
260     */
261    public RandomAccessFile getRandomAccessFile()
262    {
263        return this.randomAccessFile;
264    }
265
266    /**
267     * Checks that the instance is not closed.
268     *
269     * @throws IOException if the instance is closed.
270     */
271    private void assertNotClosed() throws IOException
272    {
273        if ( this.closed )
274        {
275            throw new IOException( this.getAlreadyClosedMessage(
276                this.getLocale() ) );
277
278        }
279    }
280
281    /**
282     * Gets a buffer for buffering streams.
283     *
284     * @return a buffer for buffering streams.
285     */
286    private byte[] getStreamBuffer()
287    {
288        if ( this.streamBuffer == null )
289        {
290            this.streamBuffer = this.getMemoryManager().
291                allocateBytes( this.getStreamBufferSize() < 0
292                               ? 0
293                               : this.getStreamBufferSize() );
294
295        }
296
297        return this.streamBuffer;
298    }
299
300    //----------------------------------------------RandomAccessFileOperations--
301    //--Messages----------------------------------------------------------------
302
303// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
304    // This section is managed by jdtaus-container-mojo.
305
306    /**
307     * Gets the text of message <code>alreadyClosed</code>.
308     * <blockquote><pre>Instanz geschlossen - keine E/A-Operationen möglich.</pre></blockquote>
309     * <blockquote><pre>Instance closed - cannot perform I/O.</pre></blockquote>
310     *
311     * @param locale The locale of the message instance to return.
312     *
313     * @return Message stating that an instance is already closed.
314     */
315    private String getAlreadyClosedMessage( final Locale locale )
316    {
317        return ContainerFactory.getContainer().
318            getMessage( this, "alreadyClosed", locale, null );
319
320    }
321
322// </editor-fold>//GEN-END:jdtausMessages
323
324    //----------------------------------------------------------------Messages--
325}