Coverage Report - org.codehaus.plexus.archiver.tar.TarFile
 
Classes in this File Line Coverage Branch Coverage Complexity
TarFile
73 %
30/41
46 %
15/32
3,083
TarFile$1
57 %
8/14
66 %
4/6
3,083
TarFile$2
50 %
2/4
N/A
3,083
 
 1  0
 package org.codehaus.plexus.archiver.tar;
 2  
 
 3  
 import java.io.File;
 4  
 import java.io.FileInputStream;
 5  
 import java.io.FilterInputStream;
 6  
 import java.io.IOException;
 7  
 import java.io.InputStream;
 8  
 import java.lang.reflect.UndeclaredThrowableException;
 9  
 import java.util.Enumeration;
 10  
 import java.util.NoSuchElementException;
 11  
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
 12  
 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
 13  
 import org.codehaus.plexus.archiver.ArchiveFile;
 14  
 import static org.codehaus.plexus.archiver.util.Streams.bufferedInputStream;
 15  
 
 16  
 /**
 17  
  * <p>
 18  
  * Implementation of {@link ArchiveFile} for tar files.</p>
 19  
  * <p>
 20  
  * Compared to
 21  
  * {@link org.apache.commons.compress.archivers.zip.ZipFile}, this one should be used with some care, due to the
 22  
  * nature of a tar file: While a zip file contains a catalog, a tar
 23  
  * file does not. In other words, the only way to read a tar file in
 24  
  * a performant manner is by iterating over it from the beginning to
 25  
  * the end. If you try to open another entry than the "next" entry,
 26  
  * then you force to skip entries, until the requested entry is found.
 27  
  * This may require to reread the entire file!</p>
 28  
  * <p>
 29  
  * In other words, the recommended use of this class is to use
 30  
  * {@link #getEntries()} and invoke {@link #getInputStream(TarArchiveEntry)}
 31  
  * only for the current entry. Basically, this is to handle it like
 32  
  * {@link TarArchiveInputStream}.</p>
 33  
  * <p>
 34  
  * The advantage of this class is that you may write code for the
 35  
  * {@link ArchiveFile}, which is valid for both tar files and zip files.</p>
 36  
  */
 37  5820
 public class TarFile
 38  
     implements ArchiveFile
 39  
 {
 40  
 
 41  
     private final java.io.File file;
 42  
 
 43  0
     private TarArchiveInputStream inputStream;
 44  
 
 45  0
     private TarArchiveEntry currentEntry;
 46  
 
 47  
     /**
 48  
      * Creates a new instance with the given file.
 49  
      */
 50  0
     public TarFile( File file )
 51  27
     {
 52  27
         this.file = file;
 53  27
     }
 54  
 
 55  
     /**
 56  
      * Implementation of {@link ArchiveFile#getEntries()}. Note, that there is
 57  
      * an interaction between this method and {@link #getInputStream(TarArchiveEntry)},
 58  
      * or {@link #getInputStream(org.apache.commons.compress.archivers.ArchiveEntry)}:
 59  
      * If an input stream is opened for any other entry than the enumerations
 60  
      * current entry, then entries may be skipped.
 61  
      */
 62  
     @Override
 63  
     public Enumeration<org.apache.commons.compress.archivers.ArchiveEntry> getEntries()
 64  
         throws IOException
 65  
     {
 66  10
         if ( inputStream != null )
 67  
         {
 68  0
             close();
 69  
         }
 70  10
         open();
 71  0
         return new Enumeration<org.apache.commons.compress.archivers.ArchiveEntry>()
 72  1168
         {
 73  
 
 74  
             boolean currentEntryValid;
 75  
 
 76  
             @Override
 77  
             public boolean hasMoreElements()
 78  
             {
 79  1168
                 if ( !currentEntryValid )
 80  
                 {
 81  
                     try
 82  
                     {
 83  1168
                         currentEntry = inputStream.getNextTarEntry();
 84  0
                     }
 85  0
                     catch ( IOException e )
 86  
                     {
 87  0
                         throw new UndeclaredThrowableException( e );
 88  1168
                     }
 89  
                 }
 90  1168
                 return currentEntry != null;
 91  
             }
 92  
 
 93  
             @Override
 94  
             public org.apache.commons.compress.archivers.ArchiveEntry nextElement()
 95  
             {
 96  1158
                 if ( currentEntry == null )
 97  
                 {
 98  0
                     throw new NoSuchElementException();
 99  
                 }
 100  1158
                 currentEntryValid = false;
 101  1158
                 return currentEntry;
 102  
             }
 103  
 
 104  
         };
 105  
     }
 106  
 
 107  
     public void close()
 108  
         throws IOException
 109  
     {
 110  4
         if ( inputStream != null )
 111  
         {
 112  4
             inputStream.close();
 113  4
             inputStream = null;
 114  
         }
 115  4
     }
 116  
 
 117  
     @Override
 118  
     public InputStream getInputStream( org.apache.commons.compress.archivers.ArchiveEntry entry )
 119  
         throws IOException
 120  
     {
 121  30
         return getInputStream( new TarArchiveEntry( entry.getName() ) );
 122  
     }
 123  
 
 124  
     /**
 125  
      * Returns an {@link InputStream} with the given entries
 126  
      * contents. This {@link InputStream} may be closed: Nothing
 127  
      * happens in that case, because an actual close would invalidate
 128  
      * the underlying {@link TarArchiveInputStream}.
 129  
      */
 130  
     public InputStream getInputStream( TarArchiveEntry entry )
 131  
         throws IOException
 132  
     {
 133  881
         if ( entry.equals( (Object) currentEntry ) && inputStream != null )
 134  
         {
 135  0
             return new FilterInputStream( inputStream )
 136  866
             {
 137  
 
 138  
                 public void close()
 139  
                     throws IOException
 140  
                 {
 141  
                     // Does nothing.
 142  881
                 }
 143  
 
 144  
             };
 145  
         }
 146  15
         return getInputStream( entry, currentEntry );
 147  
     }
 148  
 
 149  
     protected InputStream getInputStream( File file )
 150  
         throws IOException
 151  
     {
 152  11
         return new FileInputStream( file );
 153  
     }
 154  
 
 155  
     private InputStream getInputStream( TarArchiveEntry entry, TarArchiveEntry currentEntry )
 156  
         throws IOException
 157  
     {
 158  15
         if ( currentEntry == null || inputStream == null )
 159  
         {
 160  
             // Search for the entry from the beginning of the file to the end.
 161  1
             if ( inputStream != null )
 162  
             {
 163  1
                 close();
 164  
             }
 165  1
             open();
 166  1
             if ( !findEntry( entry, null ) )
 167  
             {
 168  0
                 throw new IOException( "Unknown entry: " + entry.getName() );
 169  
             }
 170  
         }
 171  
         else
 172  
         {
 173  
             // Search for the entry from the current position to the end of the file.
 174  14
             if ( findEntry( entry, null ) )
 175  
             {
 176  14
                 return getInputStream( entry );
 177  
             }
 178  0
             close();
 179  0
             open();
 180  0
             if ( !findEntry( entry, currentEntry ) )
 181  
             {
 182  0
                 throw new IOException( "No such entry: " + entry.getName() );
 183  
             }
 184  
         }
 185  1
         return getInputStream( entry );
 186  
     }
 187  
 
 188  
     private void open()
 189  
         throws IOException
 190  
     {
 191  11
         inputStream = new TarArchiveInputStream( bufferedInputStream( getInputStream( file ) ), "UTF8" );
 192  11
     }
 193  
 
 194  
     private boolean findEntry( TarArchiveEntry entry, TarArchiveEntry currentEntry )
 195  
         throws IOException
 196  
     {
 197  
         for ( ;; )
 198  
         {
 199  16
             this.currentEntry = inputStream.getNextTarEntry();
 200  16
             if ( this.currentEntry == null
 201  0
                      || ( currentEntry != null && this.currentEntry.equals( currentEntry ) ) )
 202  
             {
 203  0
                 return false;
 204  
             }
 205  16
             if ( this.currentEntry.equals( entry ) )
 206  
             {
 207  15
                 return true;
 208  
             }
 209  
         }
 210  
     }
 211  
 
 212  
 }