Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    *  jDTAUS Core Utilities
    *  Copyright (c) 2005 Christian Schulte
    *
    *  Christian Schulte, Haldener Strasse 72, 58095 Hagen, Germany
    *  <cs@jdtaus.org> (+49 2331 3543887)
    *
    *  This library is free software; you can redistribute it and/or
    *  modify it under the terms of the GNU Lesser General Public
   *  License as published by the Free Software Foundation; either
   *  version 2.1 of the License, or any later version.
   *
   *  This library is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free Software
   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   *
   */
  package org.jdtaus.core.io.util;
  
  import java.util.Locale;
  import java.util.Map;
  import java.util.TreeMap;
  import  org.jdtaus.core.container.ContainerFactory;
  import  org.jdtaus.core.container.ContextFactory;
  import  org.jdtaus.core.container.ContextInitializer;
  import  org.jdtaus.core.container.Implementation;
  import  org.jdtaus.core.container.ModelFactory;
  import  org.jdtaus.core.container.Properties;
  import  org.jdtaus.core.container.Property;
  import  org.jdtaus.core.container.PropertyException;
  import  org.jdtaus.core.io.FileOperations;
  import  org.jdtaus.core.lang.spi.MemoryManager;
  import  org.jdtaus.core.logging.spi.Logger;

Coalescing FileOperations cache.

This implementation implements a coalescing cache for FileOperations implementations. The cache is controlled by configuration property blockSize. By default property blockSize is initialized to 2097152 leading to a cache size of 10 MB (multiplied by property cacheSize which defaults to 5). All memory is allocated during instantiation so that an OutOfMemoryError may be thrown when constructing the cache but not when working with the instance.

Note:
This implementation is not thread-safe and concurrent changes to the underlying FileOperations implementation are not supported.

Author(s):
Christian Schulte
Version:
$Id: CoalescingFileOperations.java 5034 2008-07-04 11:04:09Z schulte2005 $
  
  public final class CoalescingFileOperations implements FlushableFileOperations
  {
      //--Implementation----------------------------------------------------------
  
  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausImplementation
      // This section is managed by jdtaus-container-mojo.
  
    
Meta-data describing the implementation.
  
      private static final Implementation META =
          ModelFactory.getModel().getModules().
          getImplementation(CoalescingFileOperations.class.getName());
  // </editor-fold>//GEN-END:jdtausImplementation
  
      //----------------------------------------------------------Implementation--
      //--Constructors------------------------------------------------------------
  
  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
      // This section is managed by jdtaus-container-mojo.
  
    
Initializes the properties of the instance.

Parameters:
meta the property values to initialize the instance with.
Throws:
NullPointerException if meta is null.
  
      private void initializeProperties(final Properties meta)
      {
          Property p;
  
          if(meta == null)
          {
              throw new NullPointerException("meta");
          }
  
          p = meta.getProperty("cacheSize");
          this. = ((java.lang.Integerp.getValue()).intValue();
  
 
         p = meta.getProperty("blockSize");
         this. = ((java.lang.Integerp.getValue()).intValue();
 
     }
 // </editor-fold>//GEN-END:jdtausConstructors
 
     //------------------------------------------------------------Constructors--
     //--Dependencies------------------------------------------------------------
 
 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
     // This section is managed by jdtaus-container-mojo.
 
    
Configured Logger implementation.
 
     private transient Logger dLogger;

    
Gets the configured Logger implementation.

Returns:
the configured Logger implementation.
 
     private Logger getLogger()
     {
         Logger ret = null;
         if(this. != null)
         {
             ret = this.;
         }
         else
         {
             ret = (Logger) ContainerFactory.getContainer().
                 getDependency(CoalescingFileOperations.class,
                 "Logger");
 
             if(ModelFactory.getModel().getModules().
                 getImplementation(CoalescingFileOperations.class.getName()).
                 getDependencies().getDependency("Logger").
                 isBound())
             {
                 this. = ret;
             }
         }
 
         if(ret instanceof ContextInitializer && !((ContextInitializer) ret).
             isInitialized(ContextFactory.getContext()))
         {
             ((ContextInitializer) ret).initialize(ContextFactory.getContext());
         }
 
         return ret;
     }
    
Configured MemoryManager implementation.
 
     private transient MemoryManager dMemoryManager;

    
Gets the configured MemoryManager implementation.

Returns:
the configured MemoryManager implementation.
 
     private MemoryManager getMemoryManager()
     {
         MemoryManager ret = null;
         if(this. != null)
         {
             ret = this.;
         }
         else
         {
             ret = (MemoryManager) ContainerFactory.getContainer().
                 getDependency(CoalescingFileOperations.class,
                 "MemoryManager");
 
             if(ModelFactory.getModel().getModules().
                 getImplementation(CoalescingFileOperations.class.getName()).
                 getDependencies().getDependency("MemoryManager").
                 isBound())
             {
                 this. = ret;
             }
         }
 
         if(ret instanceof ContextInitializer && !((ContextInitializer) ret).
             isInitialized(ContextFactory.getContext()))
         {
             ((ContextInitializer) ret).initialize(ContextFactory.getContext());
         }
 
         return ret;
     }
 // </editor-fold>//GEN-END:jdtausDependencies
 
     //------------------------------------------------------------Dependencies--
     //--Properties--------------------------------------------------------------
 
 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
     // This section is managed by jdtaus-container-mojo.
 
    
Property cacheSize.

Serial:
 
     private int pCacheSize;

    
Gets the value of property cacheSize.

Returns:
the value of property cacheSize.
 
     private int getCacheSize()
     {
         return this.;
     }

    
Property blockSize.

Serial:
 
     private int pBlockSize;

    
Gets the value of property blockSize.

Returns:
the value of property blockSize.
 
     public int getBlockSize()
     {
         return this.;
     }
 
 // </editor-fold>//GEN-END:jdtausProperties
 
     //--------------------------------------------------------------Properties--
     //--FileOperations----------------------------------------------------------
 
     public long getLength() throws IOException
     {
         this.assertNotClosed();
 
         return this..getLength();
     }
 
     public void setLengthfinal long newLength ) throws IOException
     {
         this.assertNotClosed();
 
         // Update the length of any cache nodes involved in the operation.
         final long oldLength = this.getLength();
         if ( newLength > oldLength )
         {
             final long delta = newLength - oldLength;
 
             assert delta <= . :
                 "Unexpected implementation limit reached.";
 
             final Node[] nodes =
                 this.getCacheNodesForLengtholdLength, ( int ) delta );
 
             for ( int i = 0; i < nodes.lengthi++ )
             {
                 final long startPos = nodes[i]. * this.getBlockSize();
                 final long blockDelta = newLength - startPos;
 
                 assert blockDelta <= . :
                     "Unexpected implementation limit reached.";
 
                 nodes[i]. = blockDelta >= this.getBlockSize()
                     ? this.getBlockSize()
                     : ( int ) blockDelta;
 
             }
         }
         else if ( newLength < oldLength )
         {
             final long delta = oldLength - newLength;
 
             assert delta <= . :
                 "Unexpected implementation limit reached.";
 
             final Node[] nodes =
                 this.getCacheNodesForLengthnewLength, ( int ) delta );
 
             for ( int i = 0; i < nodes.lengthi++ )
             {
                 final long startPos = nodes[i]. * this.getBlockSize();
                 if ( startPos > newLength )
                 { // Discard the block.
                     this..removenew Longnodes[i]. ) );
                 }
                 else
                 { // Update the blocks length.
                     final long blockDelta = newLength - startPos;
 
                     assert blockDelta <= . :
                         "Unexpected implementation limit reached.";
 
                     nodes[i]. = blockDelta >= this.getBlockSize()
                         ? this.getBlockSize()
                         : ( int ) blockDelta;
 
                 }
             }
         }
 
         this..setLength( newLength );
 
         if ( this. > newLength )
         {
             this. = newLength;
         }
     }
 
     public long getFilePointer() throws IOException
     {
         this.assertNotClosed();
 
         return this.;
     }
 
     public void setFilePointerfinal long pos ) throws IOException
     {
         this.assertNotClosed();
 
         this. = pos;
     }
 
     public int readfinal byte[] bufint offint len ) throws IOException
     {
         if ( buf == null )
         {
             throw new NullPointerException"buf" );
         }
         if ( off < 0 )
         {
             throw new IndexOutOfBoundsException( Integer.toStringoff ) );
         }
         if ( len < 0 )
         {
             throw new IndexOutOfBoundsException( Integer.toStringlen ) );
         }
         if ( off + len > buf.length )
         {
             throw new IndexOutOfBoundsException( Integer.toStringoff + len ) );
         }
 
         this.assertNotClosed();
 
         int read = FileOperations.EOF;
 
         if ( len == 0 )
         {
             read = 0;
         }
         else if ( this. < this.getLength() )
         { // End of file not reached.
             final Node[] nodes =
                 this.getCacheNodesForLengththis.len );
 
             // Ensure cache holds the data of the involved blocks.
             this.fillCachenodes );
 
             int copied = 0;
             for ( int i = 0; i < nodes.lengthi++ )
             {
                 if ( nodes[i]. == FileOperations.EOF )
                 { // Skip any end of file nodes.
                     continue;
                 }
 
                 if ( nodes[i]. != . )
                 { // Node is associated with cache memory; cache is used.
 
                     // Use the current file pointer as the starting index.
                     final long delta =
                         nodes[i]. + ( this. -
                         nodes[i]. * this.getBlockSize() );
 
                     assert delta <= . :
                         "Unexpected implementation limit reached.";
 
                     final int blockOffset = ( int ) delta;
                     final int blockDelta = nodes[i]. -
                         ( blockOffset - nodes[i]. );
 
                     final int copyLength = len > blockDelta
                         ? blockDelta
                         : len;
                     System.arraycopythis.blockOffsetbufoff,
                                       copyLength );
 
                     off += copyLength;
                     len -= copyLength;
                     copied += copyLength;
                     this. += copyLength;
                 }
                 else
                 { // Node is not associated with cache memory; read directly.
                     this..setFilePointer( this. );
                     copied += this..read( bufofflen );
                     this. += len;
 
                     this.getLogger().debug(
                         CoalescingFileOperationsBundle.getInstance().
                         getReadBypassesCacheMessage( Locale.getDefault() ).
                         formatnew Object[] {
                                 new Integerthis.getBlockSize() ),
                                 new Integerthis.getCacheSize() ),
                                 new Integerlen )
                             } ) );
 
                     break;
                 }
             }
 
             read = copied;
         }
 
         return read;
     }
 
     public void writefinal byte[] bufint offint len ) throws IOException
     {
         if ( buf == null )
         {
             throw new NullPointerException"buf" );
         }
         if ( off < 0 )
         {
             throw new IndexOutOfBoundsException( Integer.toStringoff ) );
         }
         if ( len < 0 )
         {
             throw new IndexOutOfBoundsException( Integer.toStringlen ) );
         }
         if ( off + len > buf.length )
         {
             throw new IndexOutOfBoundsException( Integer.toStringoff + len ) );
         }
 
         this.assertNotClosed();
 
         if ( this. + len > this.getLength() )
         { // Expand the file of the backing instance.
             this.setLengththis. + len );
         }
 
         final Node[] nodes =
             this.getCacheNodesForLengththis.len );
 
         // Ensure cache holds the data of the involved blocks.
         this.fillCachenodes );
 
         for ( int i = 0; i < nodes.lengthi++ )
         {
             // Check for correct file length update.
             assert nodes[i]. != FileOperations.EOF :
                 "Unexpected cache state.";
 
             if ( nodes[i]. != . )
             { // Node is associated with cache memory; cache is used.
 
                 // Use the current file pointer as the starting index.
                 final long delta = nodes[i]. + ( this. -
                     nodes[i]. * this.getBlockSize() );
 
                 assert delta <= . :
                     "Unexpected implementation limit reached.";
 
                 final int blockOffset = ( int ) delta;
                 final int blockDelta = nodes[i]. -
                     ( blockOffset - nodes[i]. );
 
                 final int copyLength = len > blockDelta
                     ? blockDelta
                     : len;
                 System.arraycopybufoffthis.blockOffsetcopyLength );
                 off += copyLength;
                 len -= copyLength;
                 this. += copyLength;
                 nodes[i]. = true;
             }
             else
             { // Node is not associated with cache memory; write out directly.
                 this..setFilePointer( this. );
                 this..write( bufofflen );
                 this. += len;
 
                 this.getLogger().debug(
                     CoalescingFileOperationsBundle.getInstance().
                     getWriteBypassesCacheMessage( Locale.getDefault() ).
                     formatnew Object[] { new Integerthis.getBlockSize() ),
                                            new Integerthis.getCacheSize() ),
                                            new Integerlen )
                         } ) );
 
                 break;
             }
         }
     }
 
     public void readfinal OutputStream out ) throws IOException
     {
         this.assertNotClosed();
 
         this..read( out );
         this. = this..getFilePointer();
     }
 
     public void writefinal InputStream in ) throws IOException
     {
         this.assertNotClosed();
 
         this..write( in );
         this. = this..getFilePointer();
     }

    
Flushes the cache and closes the FileOperations implementation backing the instance.

Throws:
IOException if flushing or closing the FileOperations implementation backing the instance fails, or if the instance already is closed.
 
     public void close() throws IOException
     {
         this.assertNotClosed();
 
         this.flush();
         this.getFileOperations().close();
         this. = true;
     }
 
     //----------------------------------------------------------FileOperations--
     //--FlushableFileOperations-------------------------------------------------
 
    
This method calls the flush() method of an underlying FlushableFileOperations implementation, if any.

Throws:
IOException if writing any pending changes fails or if the instance is closed.
 
     public void flush() throws IOException
     {
         this.assertNotClosed();
 
         this.defragmentCache();
 
         long startPos = FileOperations.EOF;
         int startIndex = FileOperations.EOF;
         int length = FileOperations.EOF;
         Node previous = null;
         boolean dirty = false;
 
         for ( Iterator it = this..entrySet().iterator(); it.hasNext();)
         {
             final Map.Entry entry = ( Map.Entry ) it.next();
             final long block = ( ( Long ) entry.getKey() ).longValue();
             final Node current = ( Node ) entry.getValue();
 
             // Skip any end of file nodes and nodes not associated with memory.
             if ( current.length == FileOperations.EOF ||
                 current.cacheIndex == . )
             {
                 continue;
             }
 
             assert current.block == block : "Unexpected cache state.";
 
             if ( previous == null )
             { // Start the first chunk.
                 previous = current;
                 startPos = current.block * this.getBlockSize();
                 startIndex = current.cacheIndex;
                 length = current.length;
                 dirty = current.dirty;
             }
             else if ( current.block == previous.block + 1L )
             { // Expand the current chunk.
 
                 assert current.cacheIndex == previous.cacheIndex +
                     this.getBlockSize() : "Unexpected cache state.";
 
                 previous = current;
                 length += current.length;
                 if ( !dirty )
                 {
                     dirty = current.dirty;
                 }
             }
             else
             { // Write out the current chunk and start a new one.
                 if ( dirty )
                 {
                     this..setFilePointer( startPos );
                     this..write(
                         this.startIndexlength );
 
                 }
 
                 previous = current;
                 startPos = current.block * this.getBlockSize();
                 startIndex = current.cacheIndex;
                 length = current.length;
                 dirty = current.dirty;
             }
         }
 
         if ( dirty )
         { // Write the remaining chunk.
             this..setFilePointer( startPos );
             this..write(
                 this.startIndexlength );
 
         }
 
         // Reset cache state.
         for ( Iterator it = this..entrySet().iterator(); it.hasNext();)
         {
             final Map.Entry entry = ( Map.Entry ) it.next();
             final Node current = ( Node ) entry.getValue();
 
             current.cacheIndex = .;
             current.dirty = false;
         }
 
         this. = 0;
 
         if ( this. instanceof FlushableFileOperations )
         { // Cache of the backing instance also needs to get flushed.
             ( ( FlushableFileOperations ) this. ).flush();
         }
     }
 
     //-------------------------------------------------FlushableFileOperations--
     //--CoalescingFileOperations------------------------------------------------
 
    
Node describing a cache block.
 
     private static final class Node
     {
 
         private static final int NO_CACHEINDEX = .;
 
         long block;
 
         int cacheIndex = ;
 
         int length;
 
         boolean dirty;
 
     }

    
The FileOperations backing the instance.
 
     private final FileOperations fileOperations;

    
Cached blocks.
 
     private final byte[] cache;

    
Second cache memory used during defragmentation.
 
     private final byte[] defragCache;

    
Index of the next free cached block.
 
     private int nextCacheIndex;

    
Maps blocks to corresponding Nodes.
 
     private final Map root = new TreeMap();

    
File pointer.
 
     private long filePointer;

    
Caches the value returned by method getFilePointerBlock.
 
     private long cachedFilePointerBlock = ;
 
     private long cachedFilePointerBlockStart = FileOperations.EOF;
 
     private static final long NO_FILEPOINTERBLOCK = .;

    
Flags the instance as beeing closed.
 
     private boolean closed;

    
Creates a new CoalescingFileOperations instance taking the FileOperations backing the instance.

Parameters:
fileOperations the FileOperations backing the instance.
Throws:
NullPointerException if fileOperations is null.
IOException if reading fails.
 
     public CoalescingFileOperationsfinal FileOperations fileOperations )
         throws IOException
     {
         super();
 
         if ( fileOperations == null )
         {
             throw new NullPointerException"fileOperations" );
         }
 
         this.initializeProperties.getProperties() );
         this.assertValidProperties();
 
         this. = fileOperations;
 
         // Pre-allocate the cache memory.
         this. = this.getMemoryManager().allocateBytes(
             this.getBlockSize() * this.getCacheSize() );
 
         this. = this.getMemoryManager().allocateBytes(
             this.getBlockSize() * this.getCacheSize() );
 
         this. = fileOperations.getFilePointer();
     }

    
Creates a new CoalescingFileOperations instance taking the FileOperations backing the instance and the number of bytes occupied by one cache block.

Parameters:
fileOperations the FileOperations backing the instance.
blockSize the number of bytes occupied by one cache block.
Throws:
NullPointerException if fileOperations is null.
PropertyException if blockSize is negative or zero.
IOException if reading fails.
 
     public CoalescingFileOperationsfinal FileOperations fileOperations,
                                       final int blockSize ) throws IOException
     {
         super();
 
         if ( fileOperations == null )
         {
             throw new NullPointerException"fileOperations" );
         }
 
         this.initializeProperties.getProperties() );
         this. = blockSize;
         this.assertValidProperties();
 
         this. = fileOperations;
 
         // Pre-allocate the cache memory.
         this. = this.getMemoryManager().allocateBytes(
             this.getBlockSize() * this.getCacheSize() );
 
         this. = this.getMemoryManager().allocateBytes(
             this.getBlockSize() * this.getCacheSize() );
 
         this. = fileOperations.getFilePointer();
     }

    
Gets the FileOperations implementation operations are performed with.

Returns:
the FileOperations implementation operations are performed with.
 
     public FileOperations getFileOperations()
     {
         return this.;
     }

    
Checks configured properties.

Throws:
PropertyException for illegal property values.
 
     private void assertValidProperties()
     {
         if ( this.getBlockSize() <= 0 )
         {
             throw new PropertyException(
                 "blockSize",
                 Integer.toStringthis.getBlockSize() ) );
 
         }
         if ( this.getCacheSize() <= 0 )
         {
             throw new PropertyException(
                 "cacheSize",
                 Integer.toStringthis.getCacheSize() ) );
 
         }
     }

    
Checks that the instance is not closed.

Throws:
IOException if the instance is closed.
 
     private void assertNotClosed() throws IOException
     {
         if ( this. )
         {
             throw new IOException( CoalescingFileOperationsBundle.getInstance().
                                    getAlreadyClosedText( Locale.getDefault() ) );
 
         }
     }

    
Gets the block pointed to by a given file pointer value.

Parameters:
filePointer the file pointer value for which to return the corresponding block.
Returns:
the block pointed to by filePointer.
 
     private long getFilePointerBlockfinal long filePointer )
     {
         if ( this. ==  )
         {
             this. =
                 ( filePointer / this.getBlockSize() ) -
                 ( ( filePointer % this.getBlockSize() ) / this.getBlockSize() );
 
             this. =
                 this. * this.getBlockSize();
 
         }
         else
         {
             if ( !( filePointer >= this. &&
                 filePointer <= this. +
                 this.getBlockSize() ) )
             {
                 this. =
                     ( filePointer / this.getBlockSize() ) -
                     ( ( filePointer % this.getBlockSize() ) /
                     this.getBlockSize() );
 
                 this. =
                     this. * this.getBlockSize();
 
             }
         }
 
         return this.;
     }

    
Gets the cache nodes for all blocks involved in a read or write operation of a given length for a given file pointer value.

Parameters:
filePointer the file pointer value to use for computing the number of involved blocks for a read or write operation of length.
length the length of the operation to perform.
Returns:
an array of cache nodes for all blocks involved in the operation in the order corresponding to the operation's needs.
 
     private Node[] getCacheNodesForLengthfinal long filePointer,
                                             final int length )
     {
         final long startingBlock = this.getFilePointerBlockfilePointer );
         final long endingBlock =
             this.getFilePointerBlockfilePointer + length );
 
         assert endingBlock - startingBlock <= . :
             "Unexpected implementation limit reached.";
 
         final Node[] nodes = new Node[ ( int ) ( endingBlock - startingBlock +
             1L ) ];
 
         if ( startingBlock == endingBlock )
         {
             nodes[0] = this.getCacheNodestartingBlock );
         }
         else
         {
             int i;
             long block;
 
             for ( block = startingBlocki = 0; block <= endingBlock;
                 block++, i++ )
             {
                 nodes[i] = this.getCacheNodeblock );
             }
         }
 
         return nodes;
     }

    
Fills the cache for a given set of cache nodes.

This method ensures that each given node gets associated with corresponding cache memory possibly flushing the cache before reading.

Parameters:
nodes the nodes to fill the cache for.
Throws:
NullPointerException if nodes is null.
IOException if reading fails.
 
     private void fillCachefinal Node[] nodes ) throws IOException
     {
         if ( nodes == null )
         {
             throw new NullPointerException"nodes" );
         }
 
         // Calculate the amount of bytes needed to be available in the cache
         // and flush the cache if nodes would not fit.
         long neededBytes = 0L;
         for ( int i = nodes.length - 1; i >= 0; i-- )
         {
             if ( nodes[i]. == . )
             { // Node's block needs to be read.
                 neededBytes += this.getBlockSize();
             }
         }
 
         if ( this. + neededBytes > this..length )
         { // Cache cannot hold the needed blocks so needs flushing.
             this.flush();
         }
 
         // Associate each node with cache memory for nodes not already
         // associated with cache memory and fill these nodes' cache memory.
         for ( int i = nodes.length - 1; i >= 0; i-- )
         {
             if ( nodes[i]. == . &&
                 this. < this..length )
             { // Node is not associated with any cache memory and can be read.
 
                 // Update the length field of the node for the block checking
                 // for a possible end of file condition.
                 long pos = nodes[i]. * this.getBlockSize();
                 if ( pos > this.getLength() )
                 { // Node is behind the end of the file.
                     nodes[i]. = FileOperations.EOF;
                     continue;
                 }
                 else if ( pos + this.getBlockSize() > this.getLength() )
                 {
                     final long delta = this.getLength() - pos;
 
                     assert delta <= . :
                         "Unexpected implementation limit reached.";
 
                     nodes[i]. = ( int ) delta;
                 }
                 else
                 {
                     nodes[i]. = this.getBlockSize();
                 }
 
                 // Associated the node with cache memory.
                 nodes[i]. = this.;
                 this. += this.getBlockSize();
 
                 // Read the node's block into cache.
                 int read = FileOperations.EOF;
                 int totalRead = 0;
                 int toRead = nodes[i].;
                 this..setFilePointer( pos );
 
                 do
                 {
                     read = this..read( this.,
                                                      nodes[i]. +
                                                      totalReadtoRead );
 
                     assert read != FileOperations.EOF :
                         "Unexpected end of file.";
 
                     totalRead += read;
                     toRead -= read;
 
                 }
                 while ( totalRead < nodes[i]. );
             }
         }
     }

    
Defragments the cache.
 
     private void defragmentCache()
     {
         int defragIndex = 0;
 
         // Step through the cached blocks and defragment the cache.
         for ( Iterator it = this..entrySet().iterator(); it.hasNext();)
         {
             final Map.Entry entry = ( Map.Entry ) it.next();
             final long block = ( ( Long ) entry.getKey() ).longValue();
             final Node current = ( Node ) entry.getValue();
 
             // Skip any end of file nodes and nodes not associated with memory.
             if ( current.length == FileOperations.EOF ||
                 current.cacheIndex == . )
             {
                 continue;
             }
 
            assert current.block == block : "Unexpected cache state.";
            System.arraycopythis.current.cacheIndexthis.,
                              defragIndexthis.getBlockSize() );
            current.cacheIndex = defragIndex;
            defragIndex += this.getBlockSize();
        }
        System.arraycopythis., 0, this., 0, this..length );
    }

    
Gets the cache node for a given block.

Parameters:
block the block to return the corresponding cache node for.
Returns:
the cache node for block.
    private Node getCacheNodefinal long block )
    {
        final Long key = new Longblock );
        Node node = ( Node ) this..getkey );
        if ( node == null )
        {
            node = new Node();
            node.block = block;
            this..putkeynode );
        }
        return node;
    }

    
Gets the cache node for a given cache index.

Parameters:
cacheIndex the index where the node's data is kept in the cache.
Returns:
the cache node for cacheIndex.
    private Node getCacheNodefinal int cacheIndex )
    {
        Node node = null;
        for ( Iterator it = this..entrySet().iterator(); it.hasNext();)
        {
            final Map.Entry entry = ( Map.Entry ) it.next();
            if ( ( ( Node ) entry.getValue() ). == cacheIndex )
            {
                node = ( Node ) entry.getValue();
                break;
            }
        }
        return node;
    }
    //------------------------------------------------CoalescingFileOperations--
New to GrepCode? Check out our FAQ X