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}