View Javadoc

1   /*
2    *  jDTAUS Core Utilities
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.io.util;
22  
23  import java.io.IOException;
24  import java.math.BigDecimal;
25  import java.util.Locale;
26  import javax.swing.event.EventListenerList;
27  import org.jdtaus.core.container.ContainerFactory;
28  import org.jdtaus.core.io.FileOperations;
29  import org.jdtaus.core.io.StructuredFile;
30  import org.jdtaus.core.io.StructuredFileListener;
31  import org.jdtaus.core.lang.spi.MemoryManager;
32  import org.jdtaus.core.messages.DeletesBlocksMessage;
33  import org.jdtaus.core.messages.InsertsBlocksMessage;
34  import org.jdtaus.core.monitor.spi.Task;
35  import org.jdtaus.core.monitor.spi.TaskMonitor;
36  
37  /**
38   * {@code StructuredFile} implementation based on {@code FileOperations}.
39   * <p>Pre {@code FlushableFileOperations} and its implementations this
40   * implementation performed read-ahead caching. This behaviour got changed
41   * in favour of {@code ReadAheadFileOperations} and
42   * {@code CoalescingFileOperations} which are generalized replacements for any
43   * cacheing formerly performed by this implementation. Since this class does
44   * not implement any cacheing anymore, the {@link #flush()} method will write
45   * out pending changes of an underlying {@code FlushableFileOperations}
46   * implementation, if any, by calling the corresponding {@code flush()} method
47   * of that {@code FlushableFileOperations} instance.</p>
48   * <p>This implementation uses task monitoring for the {@code deleteBlocks()}
49   * and {@code insertBlocks()} methods. Task monitoring is controlled by
50   * property {@code monitoringThreshold} holding the number of bytes which
51   * need to minimally be copied to enable any task monitoring during the
52   * copy operation (defaults to 5242880 - 5MB).</p>
53   *
54   * <p><b>Note:</b><br>
55   * This implementation is not thread-safe and concurrent changes to the
56   * underlying {@code FileOperations} implementation are not supported.</p>
57   *
58   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
59   * @version $JDTAUS: StructuredFileOperations.java 8641 2012-09-27 06:45:17Z schulte $
60   *
61   * @see CoalescingFileOperations
62   * @see ReadAheadFileOperations
63   */
64  public final class StructuredFileOperations implements StructuredFile
65  {
66      //--Fields------------------------------------------------------------------
67  
68      /** Pre-allocated temporary buffer. */
69      private byte[] defaultBuffer;
70  
71      /** Caches the value of property blockCount. */
72      private long cachedBlockCount = NO_CACHED_BLOCKCOUNT;
73  
74      private static final long NO_CACHED_BLOCKCOUNT = Long.MIN_VALUE;
75  
76      /** List for {@code StructuredFileListener}s. */
77      private final EventListenerList fileListeners = new EventListenerList();
78  
79      /** Value of property {@code blockSize} as a {@code BigDecimal}. */
80      private final BigDecimal decimalBlockSize;
81  
82      //------------------------------------------------------------------Fields--
83      //--Dependencies------------------------------------------------------------
84  
85  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
86      // This section is managed by jdtaus-container-mojo.
87  
88      /**
89       * Gets the configured <code>MemoryManager</code> implementation.
90       *
91       * @return The configured <code>MemoryManager</code> implementation.
92       */
93      private MemoryManager getMemoryManager()
94      {
95          return (MemoryManager) ContainerFactory.getContainer().
96              getDependency( this, "MemoryManager" );
97  
98      }
99  
100     /**
101      * Gets the configured <code>Locale</code> implementation.
102      *
103      * @return The configured <code>Locale</code> implementation.
104      */
105     private Locale getLocale()
106     {
107         return (Locale) ContainerFactory.getContainer().
108             getDependency( this, "Locale" );
109 
110     }
111 
112     /**
113      * Gets the configured <code>TaskMonitor</code> implementation.
114      *
115      * @return The configured <code>TaskMonitor</code> implementation.
116      */
117     private TaskMonitor getTaskMonitor()
118     {
119         return (TaskMonitor) ContainerFactory.getContainer().
120             getDependency( this, "TaskMonitor" );
121 
122     }
123 
124 // </editor-fold>//GEN-END:jdtausDependencies
125 
126     //------------------------------------------------------------Dependencies--
127     //--Properties--------------------------------------------------------------
128 
129 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
130     // This section is managed by jdtaus-container-mojo.
131 
132     /**
133      * Gets the value of property <code>defaultMonitoringThreshold</code>.
134      *
135      * @return Number of bytes which need to minimally be copied to enable any task monitoring during copy operations.
136      */
137     private java.lang.Integer getDefaultMonitoringThreshold()
138     {
139         return (java.lang.Integer) ContainerFactory.getContainer().
140             getProperty( this, "defaultMonitoringThreshold" );
141 
142     }
143 
144     /**
145      * Gets the value of property <code>defaultBufferSize</code>.
146      *
147      * @return Size of the pre-alocated default buffer in byte.
148      */
149     private int getDefaultBufferSize()
150     {
151         return ( (java.lang.Integer) ContainerFactory.getContainer().
152             getProperty( this, "defaultBufferSize" ) ).intValue();
153 
154     }
155 
156 // </editor-fold>//GEN-END:jdtausProperties
157 
158     //--------------------------------------------------------------Properties--
159     //--StructuredFile----------------------------------------------------------
160 
161     public int getBlockSize()
162     {
163         return this.blockSize;
164     }
165 
166     public long getBlockCount() throws IOException
167     {
168         this.assertNotClosed();
169 
170         if ( this.cachedBlockCount == NO_CACHED_BLOCKCOUNT )
171         {
172             this.cachedBlockCount = BigDecimal.valueOf(
173                 this.getFileOperations().getLength() ).divide(
174                 this.decimalBlockSize, BigDecimal.ROUND_UNNECESSARY ).
175                 longValue();
176 
177         // TODO JDK 1.5 longValueExact()
178         }
179 
180         return this.cachedBlockCount;
181     }
182 
183     public void deleteBlocks( final long index,
184                               final long count ) throws IOException
185     {
186         final long blockCount = this.getBlockCount();
187 
188         // Preconditions.
189         if ( index < 0L || index > blockCount - count )
190         {
191             throw new ArrayIndexOutOfBoundsException( (int) index );
192         }
193         if ( count <= 0 || count > blockCount - index )
194         {
195             throw new ArrayIndexOutOfBoundsException( (int) count );
196         }
197 
198         this.assertNotClosed();
199 
200         this.deleteBlocksImpl( index, count, blockCount );
201     }
202 
203     private void deleteBlocksImpl( final long index, final long count,
204                                    final long blockCount ) throws IOException
205     {
206         final long block = index + count;
207         final Task task = new Task();
208         long toMoveByte = ( blockCount - block ) * this.getBlockSize();
209         long readPos = block * this.getBlockSize();
210         long writePos = index * this.getBlockSize();
211         long progress = 0L;
212         long progressDivisor = 1L;
213         long maxProgress = toMoveByte;
214 
215         // Clear the cached block count.
216         this.cachedBlockCount = NO_CACHED_BLOCKCOUNT;
217 
218         // No blocks are following the ones to remove.
219         if ( toMoveByte == 0L )
220         {
221             this.getFileOperations().setLength(
222                 this.getFileOperations().getLength() - count *
223                                                        this.getBlockSize() );
224 
225             this.fireBlocksDeleted( index, count );
226             return;
227         }
228 
229         final byte[] buf = this.getBuffer( toMoveByte > Integer.MAX_VALUE
230                                            ? Integer.MAX_VALUE
231                                            : (int) toMoveByte );
232 
233         while ( maxProgress > Integer.MAX_VALUE )
234         {
235             maxProgress /= 2L;
236             progressDivisor *= 2L;
237         }
238 
239         task.setIndeterminate( false );
240         task.setCancelable( false );
241         task.setMinimum( 0 );
242         task.setMaximum( (int) maxProgress );
243         task.setProgress( (int) progress );
244         task.setDescription( new DeletesBlocksMessage() );
245 
246         final boolean monitoring = toMoveByte > this.getMonitoringThreshold();
247         if ( monitoring )
248         {
249             this.getTaskMonitor().monitor( task );
250         }
251 
252         try
253         {
254             // Move following blocks to the position of the first block to
255             // remove.
256             while ( toMoveByte > 0L )
257             {
258                 this.getFileOperations().setFilePointer( readPos );
259                 final int len = toMoveByte <= buf.length
260                                 ? (int) toMoveByte
261                                 : buf.length;
262 
263                 int read = 0;
264                 int total = 0;
265                 do
266                 {
267                     read = this.getFileOperations().
268                         read( buf, total, len - total );
269 
270                     assert read != FileOperations.EOF :
271                         "Unexpected end of file.";
272 
273                     total += read;
274 
275                 }
276                 while ( total < len );
277 
278                 // Move the block count blocks to the beginning.
279                 this.getFileOperations().setFilePointer( writePos );
280                 this.getFileOperations().write( buf, 0, len );
281 
282                 readPos += len;
283                 writePos += len;
284                 toMoveByte -= len;
285                 progress += len;
286 
287                 task.setProgress( (int) ( progress / progressDivisor ) );
288             }
289 
290             // Truncate the file.
291             this.getFileOperations().setLength( this.getFileOperations().
292                 getLength() - count *
293                               this.getBlockSize() );
294 
295             this.fireBlocksDeleted( index, count );
296         }
297         finally
298         {
299             if ( monitoring )
300             {
301                 this.getTaskMonitor().finish( task );
302             }
303         }
304     }
305 
306     public void insertBlocks( final long index,
307                               final long count ) throws IOException
308     {
309         final long blockCount = this.getBlockCount();
310 
311         // Preconditions.
312         if ( index < 0L || index > blockCount )
313         {
314             throw new ArrayIndexOutOfBoundsException( (int) index );
315         }
316         if ( count <= 0L || count > Long.MAX_VALUE - blockCount )
317         {
318             throw new ArrayIndexOutOfBoundsException( (int) count );
319         }
320 
321         this.assertNotClosed();
322 
323         this.insertBlocksImpl( index, count, blockCount );
324     }
325 
326     private void insertBlocksImpl( final long index, final long count,
327                                    final long blockCount ) throws IOException
328     {
329         final Task task = new Task();
330         long toMoveByte = ( blockCount - index ) * this.getBlockSize();
331         long readPos = blockCount * this.getBlockSize();
332         long writePos = readPos + count * this.getBlockSize();
333         long progress = 0L;
334         long progressDivisor = 1L;
335         long maxProgress = toMoveByte;
336 
337         // Clear the cached block count.
338         this.cachedBlockCount = NO_CACHED_BLOCKCOUNT;
339 
340         // Increase the length of the file.
341         this.getFileOperations().setLength( this.getFileOperations().
342             getLength() + this.getBlockSize() *
343                           count );
344 
345         // New blocks are inserted at the end of the file.
346         if ( toMoveByte <= 0L )
347         {
348             this.fireBlocksInserted( index, count );
349             return;
350         }
351 
352         final byte[] buf = this.getBuffer( toMoveByte > Integer.MAX_VALUE
353                                            ? Integer.MAX_VALUE
354                                            : (int) toMoveByte );
355 
356         while ( maxProgress > Integer.MAX_VALUE )
357         {
358             maxProgress /= 2L;
359             progressDivisor *= 2L;
360         }
361 
362         task.setIndeterminate( false );
363         task.setCancelable( false );
364         task.setMinimum( 0 );
365         task.setMaximum( (int) maxProgress );
366         task.setProgress( (int) progress );
367         task.setDescription( new InsertsBlocksMessage() );
368 
369         final boolean monitoring = toMoveByte > this.getMonitoringThreshold();
370         if ( monitoring )
371         {
372             this.getTaskMonitor().monitor( task );
373         }
374 
375         try
376         {
377             // Move all blocks from index inclusive count blocks to the end of
378             // the file.
379             while ( toMoveByte > 0L )
380             {
381                 final int moveLen = buf.length >= toMoveByte
382                                     ? (int) toMoveByte
383                                     : buf.length;
384 
385                 readPos -= moveLen;
386                 writePos -= moveLen;
387                 this.getFileOperations().setFilePointer( readPos );
388                 int read = 0;
389                 int total = 0;
390 
391                 do
392                 {
393                     read = this.getFileOperations().
394                         read( buf, total, moveLen - total );
395 
396                     assert read != FileOperations.EOF :
397                         "Unexpected end of file.";
398 
399                     total += read;
400 
401                 }
402                 while ( total < moveLen );
403 
404                 // Move the block count blocks to the end.
405                 this.getFileOperations().setFilePointer( writePos );
406                 this.getFileOperations().write( buf, 0, moveLen );
407 
408                 toMoveByte -= moveLen;
409                 progress += moveLen;
410 
411                 task.setProgress( (int) ( progress / progressDivisor ) );
412             }
413 
414             this.fireBlocksInserted( index, count );
415         }
416         finally
417         {
418             if ( monitoring )
419             {
420                 this.getTaskMonitor().finish( task );
421             }
422         }
423     }
424 
425     public void readBlock( final long block, final int off,
426                            final byte[] buf ) throws IOException
427     {
428         this.readBlock( block, off, buf, 0, buf.length );
429     }
430 
431     public void readBlock( final long block, final int off, final byte[] buf,
432                            final int index, final int length )
433         throws IOException
434     {
435         this.assertValidArguments( block, off, buf, index, length );
436         this.assertNotClosed();
437 
438         int totalRead = 0;
439         int toRead = length;
440 
441         this.getFileOperations().setFilePointer(
442             block * this.getBlockSize() + off );
443 
444         do
445         {
446             final int read = this.getFileOperations().
447                 read( buf, index + totalRead, toRead );
448 
449             assert read != FileOperations.EOF :
450                 "Unexpected end of file.";
451 
452             totalRead += read;
453             toRead -= read;
454 
455         }
456         while ( totalRead < length );
457     }
458 
459     public void writeBlock( final long block, final int off,
460                             final byte[] buf ) throws IOException
461     {
462         this.writeBlock( block, off, buf, 0, buf.length );
463     }
464 
465     public void writeBlock( final long block, final int off,
466                             final byte[] buf,
467                             final int index, final int length )
468         throws IOException
469     {
470         this.assertValidArguments( block, off, buf, index, length );
471         this.assertNotClosed();
472 
473         this.getFileOperations().setFilePointer(
474             block * this.getBlockSize() + off );
475 
476         this.getFileOperations().write( buf, index, length );
477     }
478 
479     /**
480      * {@inheritDoc}
481      * Flushes the instance and closes the {@code FileOperations} implementation
482      * backing the instance.
483      *
484      * @throws IOException if closing the {@code FileOperations} implementation
485      * backing the instance fails, or if the instance already is closed.
486      */
487     public void close() throws IOException
488     {
489         this.assertNotClosed();
490 
491         this.flush();
492         this.getFileOperations().close();
493         this.closed = true;
494     }
495 
496     public void addStructuredFileListener(
497         final StructuredFileListener listener )
498     {
499         this.fileListeners.add( StructuredFileListener.class, listener );
500     }
501 
502     public void removeStructuredFileListener(
503         final StructuredFileListener listener )
504     {
505         this.fileListeners.remove( StructuredFileListener.class, listener );
506     }
507 
508     public StructuredFileListener[] getStructuredFileListeners()
509     {
510         return (StructuredFileListener[]) this.fileListeners.getListeners(
511             StructuredFileListener.class );
512 
513     }
514 
515     //----------------------------------------------------------StructuredFile--
516     //--StructuredFileOperations------------------------------------------------
517 
518     /** Number of bytes per block. */
519     private int blockSize;
520 
521     /** Mininum number of bytes to copy to start any task monitoring. */
522     private Integer monitoringThreshold;
523 
524     /** {@code FileOperations} backing the instance. */
525     private FileOperations fileOperations;
526 
527     /** Flags the instance as beeing closed. */
528     private boolean closed;
529 
530     /**
531      * Creates a new {@code StructuredFileOperations} instance taking the size
532      * of one block in byte and the {@code FileOperations} operations are to be
533      * performed with.
534      *
535      * @param blockSize Number of bytes per block.
536      * @param fileOperations {@code FileOperations} implementation to operate
537      * on.
538      *
539      * @throws NullPointerException if {@code fileOperations} is {@code null}.
540      * @throws IllegalArgumentException if {@code blockSize} is incompatible
541      * with the length of {@code fileOperations}.
542      * @throws IOException if getting the length from the {@code fileOperations}
543      * fails.
544      */
545     public StructuredFileOperations( final int blockSize,
546                                      final FileOperations fileOperations )
547         throws IOException
548     {
549         super();
550 
551         if ( fileOperations == null )
552         {
553             throw new NullPointerException( "fileOperations" );
554         }
555         if ( blockSize <= 0 )
556         {
557             throw new IllegalArgumentException( Integer.toString( blockSize ) );
558         }
559 
560         this.blockSize = blockSize;
561         this.decimalBlockSize = BigDecimal.valueOf( blockSize );
562         this.fileOperations = fileOperations;
563         this.assertValidFileLength();
564     }
565 
566     /**
567      * Creates a new {@code StructuredFileOperations} instance taking the size
568      * of one block in byte, task monitoring configuration and the
569      * {@code FileOperations} operations are to be performed with.
570      *
571      * @param blockSize Number of bytes per block.
572      * @param monitoringThreshold the mininum number of bytes to copy to start
573      * any task monitoring.
574      * @param fileOperations {@code FileOperations} implementation to operate
575      * on.
576      *
577      * @throws NullPointerException if {@code fileOperations} is {@code null}.
578      * @throws IllegalArgumentException if {@code blockSize} is incompatible
579      * with the length of {@code fileOperations}.
580      * @throws IOException if getting the length from the {@code fileOperations}
581      * fails.
582      */
583     public StructuredFileOperations( final int blockSize,
584                                      final int monitoringThreshold,
585                                      final FileOperations fileOperations )
586         throws IOException
587     {
588         super();
589 
590         if ( fileOperations == null )
591         {
592             throw new NullPointerException( "fileOperations" );
593         }
594         if ( blockSize <= 0 )
595         {
596             throw new IllegalArgumentException( Integer.toString( blockSize ) );
597         }
598 
599         this.blockSize = blockSize;
600         this.decimalBlockSize = BigDecimal.valueOf( blockSize );
601         this.fileOperations = fileOperations;
602 
603         if ( monitoringThreshold > 0 )
604         {
605             this.monitoringThreshold = new Integer( monitoringThreshold );
606         }
607 
608         this.assertValidFileLength();
609     }
610 
611     /**
612      * Gets the {@code FileOperations} implementation operations are performed
613      * with.
614      *
615      * @return the {@code FileOperations} implementation operations are
616      * performed with.
617      */
618     public FileOperations getFileOperations()
619     {
620         return this.fileOperations;
621     }
622 
623     /**
624      * Calls the {@code flush()} method of an underlying
625      * {@code FlushableFileOperations} instance, if any.
626      *
627      * @throws IOException if reading or writing fails.
628      */
629     public void flush() throws IOException
630     {
631         this.assertNotClosed();
632 
633         if ( this.getFileOperations() instanceof FlushableFileOperations )
634         {
635             ( (FlushableFileOperations) this.getFileOperations() ).flush();
636         }
637     }
638 
639     /**
640      * Checks arguments provided to the {@code readBlock} and {@code writeBlock}
641      * methods.
642      *
643      * @throws NullPointerException if {@code buf} is {@code null}.
644      * @throws IndexOutOfBoundsException if {@code block} is negative,
645      * greater than or equal to {@code getBlockCount()}, or {@code off} is
646      * negative, greater than or equal to {@code getBlockSize()}, or
647      * {@code index} is negative, greater than or equal to the length of
648      * {@code buf}, or {@code length} is negative or greater than the
649      * length of {@code buf} minus {@code index} or greater than
650      * {@code getBlockSize() minus {@code off}.
651      */
652     private void assertValidArguments( final long block, final int off,
653                                        final byte[] buf, final int index,
654                                        final int length ) throws
655         NullPointerException, IndexOutOfBoundsException, IOException
656     {
657         final long blockCount = this.getBlockCount();
658 
659         if ( buf == null )
660         {
661             throw new NullPointerException( "buf" );
662         }
663         if ( block < 0 || block >= blockCount )
664         {
665             throw new ArrayIndexOutOfBoundsException( (int) block );
666         }
667         if ( off < 0 || off >= this.getBlockSize() )
668         {
669             throw new ArrayIndexOutOfBoundsException( off );
670         }
671         if ( index < 0 || index >= buf.length )
672         {
673             throw new ArrayIndexOutOfBoundsException( index );
674         }
675         if ( length < 0L || length > buf.length - index ||
676              length > this.getBlockSize() - off )
677         {
678             throw new ArrayIndexOutOfBoundsException( length );
679         }
680     }
681 
682     /**
683      * Checks the length of the provided {@code FileOperations} implementation
684      * against property {@code blockSize}.
685      *
686      * @throws IllegalArgumentException if the combination of property
687      * {@code blockSize} and {@code getFileOperations().getLength()} is invalid.
688      * @throws IOException if the getting the length fails.
689      */
690     private void assertValidFileLength() throws IOException
691     {
692         if ( this.getFileOperations() != null &&
693              this.getFileOperations().getLength() % this.getBlockSize() != 0L )
694         {
695             throw new IllegalArgumentException(
696                 Long.toString( this.getFileOperations().getLength() %
697                                this.getBlockSize() ) );
698 
699         }
700     }
701 
702     /**
703      * Checks that the instance is not closed.
704      *
705      * @throws IOException if the instance is closed.
706      */
707     private void assertNotClosed() throws IOException
708     {
709         if ( this.closed )
710         {
711             throw new IOException( this.getAlreadyClosedMessage(
712                 this.getLocale() ) );
713 
714         }
715     }
716 
717     /**
718      * Gets the value of property {@code monitoringThreshold}.
719      *
720      * @return the mininum number of bytes to copy to start any task monitoring.
721      */
722     public int getMonitoringThreshold()
723     {
724         if ( this.monitoringThreshold == null )
725         {
726             this.monitoringThreshold = this.getDefaultMonitoringThreshold();
727         }
728 
729         return this.monitoringThreshold.intValue();
730     }
731 
732     /**
733      * Notifies all registered {@code StructuredFileListener}s about inserted
734      * blocks.
735      *
736      * @param index the index new blocks were inserted.
737      * @param insertedBlocks the number of blocks which were inserted at
738      * {@code index}.
739      *
740      * @throws IOException if reading or writing fails.
741      */
742     private void fireBlocksInserted(
743         final long index, final long insertedBlocks ) throws IOException
744     {
745         final Object[] listeners = this.fileListeners.getListenerList();
746         for ( int i = listeners.length - 2; i >= 0; i -= 2 )
747         {
748             if ( listeners[i] == StructuredFileListener.class )
749             {
750                 ( (StructuredFileListener) listeners[i + 1] ).blocksInserted(
751                     index, insertedBlocks );
752 
753             }
754         }
755     }
756 
757     /**
758      * Notifies all registered {@code StructuredFileListener}s about deleted
759      * blocks.
760      *
761      * @param index the index blocks were deleted at.
762      * @param deletedBlocks the number of blocks which were deleted starting at
763      * {@code index}.
764      *
765      * @throws IOException if reading or writing fails.
766      */
767     private void fireBlocksDeleted(
768         final long index, final long deletedBlocks ) throws IOException
769     {
770         final Object[] listeners = this.fileListeners.getListenerList();
771         for ( int i = listeners.length - 2; i >= 0; i -= 2 )
772         {
773             if ( listeners[i] == StructuredFileListener.class )
774             {
775                 ( (StructuredFileListener) listeners[i + 1] ).blocksDeleted(
776                     index, deletedBlocks );
777 
778             }
779         }
780     }
781 
782     private byte[] getBuffer( final int requested ) throws IOException
783     {
784         final long length = this.getFileOperations().getLength();
785 
786         if ( requested <= 0 || requested > length )
787         {
788             throw new IllegalArgumentException( Integer.toString( requested ) );
789         }
790 
791         if ( this.defaultBuffer == null )
792         {
793             this.defaultBuffer = this.getMemoryManager().
794                 allocateBytes( this.getDefaultBufferSize() );
795 
796         }
797 
798         return requested <= this.defaultBuffer.length ||
799                this.getMemoryManager().getAvailableBytes() < requested
800                ? this.defaultBuffer
801                : this.getMemoryManager().allocateBytes( requested );
802 
803     }
804 
805     //------------------------------------------------StructuredFileOperations--
806     //--Messages----------------------------------------------------------------
807 
808 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
809     // This section is managed by jdtaus-container-mojo.
810 
811     /**
812      * Gets the text of message <code>alreadyClosed</code>.
813      * <blockquote><pre>Instanz geschlossen - keine E/A-Operationen möglich.</pre></blockquote>
814      * <blockquote><pre>Instance closed - cannot perform I/O.</pre></blockquote>
815      *
816      * @param locale The locale of the message instance to return.
817      *
818      * @return Message stating that an instance is already closed.
819      */
820     private String getAlreadyClosedMessage( final Locale locale )
821     {
822         return ContainerFactory.getContainer().
823             getMessage( this, "alreadyClosed", locale, null );
824 
825     }
826 
827 // </editor-fold>//GEN-END:jdtausMessages
828 
829     //----------------------------------------------------------------Messages--
830 }