org.codehaus.plexus.archiver.tar.TarFile Maven / Gradle / Ivy
package org.codehaus.plexus.archiver.tar;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.codehaus.plexus.archiver.ArchiveFile;
import org.codehaus.plexus.archiver.util.Streams;
import static org.codehaus.plexus.archiver.util.Streams.bufferedInputStream;
/**
*
* Implementation of {@link ArchiveFile} for tar files.
*
* Compared to
* {@link org.apache.commons.compress.archivers.zip.ZipFile}, this one should be used with some care, due to the
* nature of a tar file: While a zip file contains a catalog, a tar
* file does not. In other words, the only way to read a tar file in
* a performant manner is by iterating over it from the beginning to
* the end. If you try to open another entry than the "next" entry,
* then you force to skip entries, until the requested entry is found.
* This may require to reread the entire file!
*
* In other words, the recommended use of this class is to use
* {@link #getEntries()} and invoke {@link #getInputStream(TarArchiveEntry)}
* only for the current entry. Basically, this is to handle it like
* {@link TarArchiveInputStream}.
*
* The advantage of this class is that you may write code for the
* {@link ArchiveFile}, which is valid for both tar files and zip files.
*/
public class TarFile implements ArchiveFile {
private final java.io.File file;
private TarArchiveInputStream inputStream;
private TarArchiveEntry currentEntry;
/**
* Creates a new instance with the given file.
*/
public TarFile(File file) {
this.file = file;
}
/**
* Implementation of {@link ArchiveFile#getEntries()}. Note, that there is
* an interaction between this method and {@link #getInputStream(TarArchiveEntry)},
* or {@link #getInputStream(org.apache.commons.compress.archivers.ArchiveEntry)}:
* If an input stream is opened for any other entry than the enumerations
* current entry, then entries may be skipped.
*/
@Override
public Enumeration getEntries() throws IOException {
if (inputStream != null) {
close();
}
open();
return new Enumeration() {
boolean currentEntryValid;
@Override
public boolean hasMoreElements() {
if (!currentEntryValid) {
try {
currentEntry = inputStream.getNextTarEntry();
} catch (IOException e) {
throw new UndeclaredThrowableException(e);
}
}
return currentEntry != null;
}
@Override
public org.apache.commons.compress.archivers.ArchiveEntry nextElement() {
if (currentEntry == null) {
throw new NoSuchElementException();
}
currentEntryValid = false;
return currentEntry;
}
};
}
public void close() throws IOException {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
}
@Override
public InputStream getInputStream(org.apache.commons.compress.archivers.ArchiveEntry entry) throws IOException {
return getInputStream(new TarArchiveEntry(entry.getName()));
}
/**
* Returns an {@link InputStream} with the given entries
* contents. This {@link InputStream} may be closed: Nothing
* happens in that case, because an actual close would invalidate
* the underlying {@link TarArchiveInputStream}.
*/
public InputStream getInputStream(TarArchiveEntry entry) throws IOException {
if (entry.equals((Object) currentEntry) && inputStream != null) {
return new FilterInputStream(inputStream) {
public void close() throws IOException {
// Does nothing.
}
};
}
return getInputStream(entry, currentEntry);
}
protected InputStream getInputStream(File file) throws IOException {
return Streams.fileInputStream(file);
}
private InputStream getInputStream(TarArchiveEntry entry, TarArchiveEntry currentEntry) throws IOException {
if (currentEntry == null || inputStream == null) {
// Search for the entry from the beginning of the file to the end.
if (inputStream != null) {
close();
}
open();
if (!findEntry(entry, null)) {
throw new IOException("Unknown entry: " + entry.getName());
}
} else {
// Search for the entry from the current position to the end of the file.
if (findEntry(entry, null)) {
return getInputStream(entry);
}
close();
open();
if (!findEntry(entry, currentEntry)) {
throw new IOException("No such entry: " + entry.getName());
}
}
return getInputStream(entry);
}
private void open() throws IOException {
inputStream = new TarArchiveInputStream(bufferedInputStream(getInputStream(file)), "UTF8");
}
private boolean findEntry(TarArchiveEntry entry, TarArchiveEntry currentEntry) throws IOException {
for (; ; ) {
this.currentEntry = inputStream.getNextTarEntry();
if (this.currentEntry == null || (currentEntry != null && this.currentEntry.equals(currentEntry))) {
return false;
}
if (this.currentEntry.equals(entry)) {
return true;
}
}
}
}