de.unkrig.commons.file.CompressUtil Maven / Gradle / Ivy
Show all versions of de-unkrig-commons Show documentation
/*
* de.unkrig.commons - A general-purpose Java class library
*
* Copyright (c) 2014, Arno Unkrig
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package de.unkrig.commons.file;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.compressors.CompressorInputStream;
import de.unkrig.commons.file.org.apache.commons.compress.archivers.ArchiveFormat;
import de.unkrig.commons.file.org.apache.commons.compress.archivers.ArchiveFormatFactory;
import de.unkrig.commons.file.org.apache.commons.compress.compressors.CompressionFormat;
import de.unkrig.commons.file.org.apache.commons.compress.compressors.CompressionFormatFactory;
import de.unkrig.commons.io.IoUtil;
import de.unkrig.commons.io.MarkableFileInputStream;
import de.unkrig.commons.lang.AssertionUtil;
import de.unkrig.commons.lang.ExceptionUtil;
import de.unkrig.commons.lang.protocol.Predicate;
import de.unkrig.commons.nullanalysis.Nullable;
/** Utility class which implements functionality that is related to {@code org.apache.commons.compress}. */
public final
class CompressUtil {
static { AssertionUtil.enableAssertionsForThisClass(); }
private static final Logger LOGGER = Logger.getLogger(CompressUtil.class.getName());
private CompressUtil() {}
/**
* @param {@link #handleArchive(ArchiveInputStream, ArchiveFormat)}
* returns a value of this type, which is, in turn, returned
* by the {@code processStream()} and {@code processValue()}
* methods
* @see #handleArchive(ArchiveInputStream, ArchiveFormat)
*/
public
interface ArchiveHandler {
/**
* May or may not read entries and data from the {@code archiveInputStream}, and may or may not close it.
*
* @see #processFile(File, Predicate, ArchiveHandler, Predicate, CompressorHandler, NormalContentsHandler)
* @see #processStream(InputStream, Predicate, ArchiveHandler, Predicate, CompressorHandler,
* NormalContentsHandler)
*/
@Nullable T
handleArchive(ArchiveInputStream archiveInputStream, ArchiveFormat archiveFormat) throws IOException;
}
/**
* @param {@link #handleCompressor(CompressorInputStream,
* CompressionFormat)} returns a value of this
* type, which is, in turn, returned by the {@code
* processStream()} and {@code processValue()}
* methods
* @see #handleCompressor(CompressorInputStream, CompressionFormat)
*/
public
interface CompressorHandler {
/**
* May or may not read from the {@code compressorInputStream}.
*
* @see #processFile(File, Predicate, ArchiveHandler, Predicate, CompressorHandler, NormalContentsHandler)
* @see #processStream(InputStream, Predicate, ArchiveHandler, Predicate, CompressorHandler,
* NormalContentsHandler)
*/
@Nullable T
handleCompressor(CompressorInputStream compressorInputStream, CompressionFormat compressionFormat)
throws IOException;
}
/**
* @param {@link #handleNormalContents(InputStream)} returns a value of this type,
* which is, in turn, returned by the {@code processStream()} and {@code
* processValue()} methods
* @see #handleNormalContents(InputStream)
*/
public
interface NormalContentsHandler {
/**
* May or may not read from the {@code inputStream}, and may or may not close it.
*
* @see #processFile(File, Predicate, ArchiveHandler, Predicate, CompressorHandler, NormalContentsHandler)
* @see CompressUtil#processStream(InputStream, Predicate, ArchiveHandler, Predicate, CompressorHandler,
* NormalContentsHandler)
*/
@Nullable T
handleNormalContents(InputStream inputStream) throws IOException;
}
/**
* Invokes exactly one of {@code archiveHandler}, {@code compressorHandler} or {@code
* normalContentsHandler}.
*
* An archive file is introspected iff {@code lookIntoFormat} evaluates to {@code true} for {@code
* "archive-format-name:path"}.
*
* A compressed file is introspected iff {@code lookIntoFormat} evaluates to {@code true} for {@code
* "compression-format-name:path"}.
*
* @see ArchiveFormatFactory#allFormats()
* @see CompressionFormatFactory#allFormats()
*/
@Nullable public static T
processStream(
String path,
InputStream inputStream,
Predicate super String> lookIntoFormat,
ArchiveHandler extends T> archiveHandler,
CompressorHandler extends T> compressorHandler,
NormalContentsHandler extends T> normalContentsHandler
) throws IOException {
return CompressUtil.processStream(
inputStream, // inputStream
CompressUtil.lookIntoArchive(path, lookIntoFormat), // lookIntoArchive
archiveHandler, // archiveHandler
CompressUtil.lookIntoCompressed(path, lookIntoFormat), // lookIntoCompressed
compressorHandler, // compressorHandler
normalContentsHandler // normalContentsHandler
);
}
/**
* Invokes exactly one of {@code archiveHandler}, {@code compressorHandler} or {@code
* normalContentsHandler}.
*
* @param lookIntoArchive An archive stream is introspected iff {@code lookIntoArchive} evaluates to {@code true}
* for the archive format
* @param lookIntoCompressed A compressed stream is introspected iff {@code lookIntoCompressed} evaluates to {@code
* true} for the compression format
* @see CompressionFormatFactory#allFormats()
*/
@Nullable public static T
processStream(
InputStream inputStream,
Predicate super ArchiveFormat> lookIntoArchive,
ArchiveHandler extends T> archiveHandler,
Predicate super CompressionFormat> lookIntoCompressed,
CompressorHandler extends T> compressorHandler,
NormalContentsHandler extends T> normalContentsHandler
) throws IOException {
if (!inputStream.markSupported()) inputStream = new BufferedInputStream(inputStream);
ARCHIVE: {
final ArchiveFormat archiveFormat = ArchiveFormatFactory.forContents(inputStream);
if (archiveFormat == null) break ARCHIVE;
if (!lookIntoArchive.evaluate(archiveFormat)) {
return normalContentsHandler.handleNormalContents(inputStream);
}
ArchiveInputStream ais;
try {
ais = archiveFormat.archiveInputStream(IoUtil.unclosableInputStream(inputStream));
} catch (ArchiveException ae) {
throw new IOException(archiveFormat.getName(), ae);
}
return archiveHandler.handleArchive(ais, archiveFormat);
}
COMPRESSED: {
final CompressionFormat compressionFormat = CompressionFormatFactory.forContents(inputStream);
if (compressionFormat == null) break COMPRESSED;
if (!lookIntoCompressed.evaluate(compressionFormat)) {
return normalContentsHandler.handleNormalContents(inputStream);
}
return compressorHandler.handleCompressor(
compressionFormat.compressorInputStream(IoUtil.unclosableInputStream(inputStream)),
compressionFormat
);
}
return normalContentsHandler.handleNormalContents(inputStream);
}
/**
* Invokes exactly one of {@code archiveHandler}, {@code compressorHandler} or {@code
* normalContentsHandler}.
*
* An archive file is introspected iff {@code lookIntoFormat} evaluates to {@code true} for
* "{@code archive-format-name}{@code :}{@code path}".
*
* A compressed file is introspected iff {@code lookIntoFormat} evaluates to {@code true} for "{@code
* compression-format-name}{@code :}{@code path}".
*
* @see ArchiveFormatFactory#allFormats()
* @see CompressionFormatFactory#allFormats()
*/
@Nullable public static T
processFile(
String path,
File file,
Predicate super String> lookIntoFormat,
ArchiveHandler extends T> archiveHandler,
CompressorHandler extends T> compressorHandler,
NormalContentsHandler extends T> normalContentsHandler
) throws IOException {
return CompressUtil.processFile(
file,
CompressUtil.lookIntoArchive(path, lookIntoFormat),
archiveHandler,
CompressUtil.lookIntoCompressed(path, lookIntoFormat),
compressorHandler,
normalContentsHandler
);
}
/**
* Invokes exactly one of {@code archiveHandler}, {@code compressorHandler} or {@code
* normalContentsHandler}.
*
* @param lookIntoArchive An archive file is introspected iff {@code lookIntoArchive} evaluates to {@code true}
* for the archive format
* @param lookIntoCompressed A compressed file is introspected iff {@code lookIntoCompressed} evaluates to {@code
* true} for the compression format
* @see CompressionFormatFactory#allFormats()
*/
@Nullable public static T
processFile(
final File file,
Predicate super ArchiveFormat> lookIntoArchive,
ArchiveHandler extends T> archiveHandler,
Predicate super CompressionFormat> lookIntoCompressed,
CompressorHandler extends T> compressorHandler,
NormalContentsHandler extends T> normalContentsHandler
) throws IOException {
InputStream is = new MarkableFileInputStream(file);
try {
ArchiveFormat archiveFormat = ArchiveFormatFactory.forContents(is);
if (archiveFormat != null) {
if (!lookIntoArchive.evaluate(archiveFormat)) {
return normalContentsHandler.handleNormalContents(is);
}
is.close();
ArchiveInputStream ais = archiveFormat.open(file);
is = ais;
T result = archiveHandler.handleArchive(ais, archiveFormat);
ais.close();
return result;
}
CompressionFormat compressionFormat = CompressionFormatFactory.forContents(is);
if (compressionFormat != null) {
if (!lookIntoCompressed.evaluate(compressionFormat)) {
return normalContentsHandler.handleNormalContents(is);
}
CompressorInputStream cis = compressionFormat.compressorInputStream(is);
T result = compressorHandler.handleCompressor(cis, compressionFormat);
cis.close();
return result;
}
CompressUtil.LOGGER.log(Level.FINER, "Processing normal file \"{0}\"", file);
T result = normalContentsHandler.handleNormalContents(is);
is.close();
return result;
} catch (ArchiveException ae) {
throw ExceptionUtil.wrap(file.toString(), ae, IOException.class);
} catch (IOException ioe) {
throw ExceptionUtil.wrap(file.toString(), ioe);
} finally {
try { is.close(); } catch (Exception e) {}
}
}
private static Predicate
lookIntoArchive(final String path, final Predicate super String> lookIntoFormat) {
return new Predicate() {
@Override public boolean
evaluate(ArchiveFormat af) {
boolean result = lookIntoFormat.evaluate(af.getName() + ':' + path);
CompressUtil.LOGGER.log(
Level.FINER,
"Look into archive \"{0}\"? => {1}",
new Object[] { path, result }
);
return result;
}
};
}
private static Predicate
lookIntoCompressed(final String path, final Predicate super String> lookIntoFormat) {
return new Predicate() {
@Override public boolean
evaluate(CompressionFormat cf) {
boolean result = lookIntoFormat.evaluate(cf.getName() + ':' + path);
CompressUtil.LOGGER.log(
Level.FINER,
"Look into compressed \"{0}\"? => {1}",
new Object[] { path, result }
);
return result;
}
};
}
}