ZipFileArchiver.java

package org.jbehave.io;

import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
import static org.apache.commons.lang3.StringUtils.remove;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * File archiver for zip files
 */
public class ZipFileArchiver implements FileArchiver {

    private static final String UNIX_SEPARATOR = "/";
    private static final String ARCHIVER_NAME = "zip";
    private ArchiveStreamFactory factory = new ArchiveStreamFactory();

    public boolean isArchive(File file) {
        return fileExtension(file).equalsIgnoreCase("zip");
    }

    private String fileExtension(File file) {
        return StringUtils.substringAfterLast(file.getName(), ".");
    }

    public void archive(File archive, File directory) {
        try {
            ArchiveOutputStream out = factory.createArchiveOutputStream(ARCHIVER_NAME,
                    new FileOutputStream(archive));
            List<File> files = listContent(directory);
            for (File file : files) {
                if (!file.isDirectory()) {
                    ZipArchiveEntry entry = new ZipArchiveEntry(relativeTo(
                            file, directory).getPath());
                    zipEntry(entry, out, file);
                }
            }
            out.close();
        } catch (Exception e) {
            throw new FileArchiveFailedException(archive, directory, e);
        }
    }

    public File relativeTo(File file, File directory) {
        String filePath = separatorsToUnix(file.getPath());
        String directoryPath = separatorsToUnix(directory.getPath());
        if (!directoryPath.endsWith(UNIX_SEPARATOR)) {
            // ensure directory has a trailing separator
            // that will be removed from the full file path
            directoryPath = directoryPath + UNIX_SEPARATOR;
        }
        return new File(remove(filePath, directoryPath));
    }

    private void zipEntry(ZipArchiveEntry entry, ArchiveOutputStream out,
            File file) throws IOException, FileNotFoundException {
        out.putArchiveEntry(entry);
        IOUtils.copy(new FileInputStream(file), out);
        out.closeArchiveEntry();
    }

    public File directoryOf(File archive) {
        return new File(pathWithoutExtension(archive));
    }

    private String pathWithoutExtension(File file) {
        return StringUtils.substringBeforeLast(file.getPath(), ".");
    }

    public void unarchive(File archive, File directory) {
        InputStream is = null;
        ArchiveInputStream in = null;
        try {
            is = new FileInputStream(archive);
            in = factory.createArchiveInputStream(ARCHIVER_NAME, is);
            ZipArchiveEntry entry = null;
            while ((entry = (ZipArchiveEntry) in.getNextEntry()) != null) {
                unzipEntry(entry, in, directory);
            }
        } catch (Exception e) {
            throw new FileUnarchiveFailedException(archive, directory, e);
        } finally {
            close(is);
            close(in);
        }
    }

    private void close(InputStream is) {
        try {
            if (is != null) {
                is.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<File> listContent(File file) {
        List<File> content = new ArrayList<File>();
        content.add(file);
        if (file.isDirectory()) {
            for (File child : file.listFiles()) {
                content.addAll(listContent(child));
            }
        }
        return content;
    }

    private void unzipEntry(ZipArchiveEntry entry, InputStream in,
            File directory) throws IOException {

        if (entry.isDirectory()) {
            createDir(new File(directory, entry.getName()));
            return;
        }

        File outputFile = new File(directory, entry.getName());
        createDir(outputFile.getParentFile());
        copy(entry, in, outputFile);

    }

    private void createDir(File dir) throws IOException {
        if (dir.exists()) {
            return;
        }
        if (!dir.mkdirs()) {
            throw new IOException("Failed to create dir " + dir);
        }
    }

    private void copy(ZipArchiveEntry entry, InputStream in, File outputFile)
            throws FileNotFoundException, IOException {
        OutputStream out = new FileOutputStream(outputFile);
        try {
            IOUtils.copy(in, out);
        } finally {
            out.close();
        }
    }

}