es.ucm.fdi.util.archive.ZipFormat.java Source code

Java tutorial

Introduction

Here is the source code for es.ucm.fdi.util.archive.ZipFormat.java

Source

/**
 * AC - A source-code copy detector
 *
 *     For more information please visit:  http://github.com/manuel-freire/ac
 *
 * ****************************************************************************
 *
 * This file is part of AC, version 2.0
 *
 * AC 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 3 of the License,
 * or (at your option) any later version.
 *
 * AC 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 AC.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * ZipFormat.java
 *
 * Created on September 18, 2006, 6:25 PM
 *
 */

package es.ucm.fdi.util.archive;

import es.ucm.fdi.util.FileUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;

/**
 * Manages the popular Zip format (PKZIP, Jar, War); uses Ant's buit-in
 * unzipper, which is more tolerant than the util.zip one.
 *
 * @author mfreire
 */
public class ZipFormat implements ArchiveFormat {

    private static byte[] zipMagic = new byte[] { 0x50, 0x4b }; // 0x03, 0x30 both found

    public String getArchiveExtensions() {
        return "(.*\\.zip)|(.*\\.jar)|(.*\\.war)";
    }

    private void assertIsZip(File source) throws IOException {
        try {
            if (!FileUtils.startMatches(new FileInputStream(source), zipMagic, 0)) {
                throw new IOException("File is not a zip archive");
            }
        } catch (IOException ioe) {
            throw new IOException("File is not a zip archive", ioe);
        }
    }

    public ArrayList<String> list(File source) throws IOException {
        assertIsZip(source);

        ArrayList<String> paths = new ArrayList<String>();
        try (ZipFile zf = new ZipFile(source)) {
            Enumeration entries = zf.getEntries();
            while (entries.hasMoreElements()) {
                ZipArchiveEntry e = (ZipArchiveEntry) entries.nextElement();

                String name = FileUtils.toCanonicalPath(e.getName());
                if (e.isDirectory()) {
                    continue;
                }

                paths.add(name);
            }
        }
        return paths;
    }

    public void expand(File source, File destDir) throws IOException {
        assertIsZip(source);

        try (ZipFile zf = new ZipFile(source)) {
            byte[] b = new byte[512];

            Enumeration entries = zf.getEntries();
            while (entries.hasMoreElements()) {
                ZipArchiveEntry e = (ZipArchiveEntry) entries.nextElement();
                //log.debug("Extracting zip: "+ficheroZip.getName());

                // baskslash-protection: zip format expects only 'fw' slashes
                String name = FileUtils.toCanonicalPath(e.getName());

                if (e.isDirectory()) {
                    //log.debug("\tExtracting directory "+e.getName());
                    File dir = new File(destDir, name);
                    dir.mkdirs();
                    continue;
                }

                //log.debug("\tExtracting file "+name);
                File outFile = new File(destDir, name);
                if (!outFile.getParentFile().exists()) {
                    //log.warn("weird zip: had to create parent: "+outFile.getParentFile());
                    outFile.getParentFile().mkdirs();
                }
                try (FileOutputStream fos = new FileOutputStream(outFile); InputStream is = zf.getInputStream(e)) {
                    int len;
                    while ((len = is.read(b)) != -1) {
                        fos.write(b, 0, len);
                    }
                }
            }
        }
    }

    public boolean extractOne(File source, String path, File dest) throws IOException {
        assertIsZip(source);

        try (ZipFile zf = new ZipFile(source)) {
            byte[] b = new byte[512];

            //log.debug("Extracting zip: "+ficheroZip.getName());
            Enumeration entries = zf.getEntries();
            while (entries.hasMoreElements()) {
                ZipArchiveEntry e = (ZipArchiveEntry) entries.nextElement();

                // baskslash-protection: zip format expects only 'fw' slashes
                String name = FileUtils.toCanonicalPath(e.getName());

                //                System.err.println(" "+name+" =? "+path);
                if (!name.equals(path) || e.isDirectory())
                    continue;

                if (!dest.getParentFile().exists()) {
                    //log.warn("weird zip: had to create parent: "+outFile.getParentFile());
                    dest.getParentFile().mkdirs();
                }

                try (FileOutputStream fos = new FileOutputStream(dest); InputStream is = zf.getInputStream(e)) {
                    int len;
                    while ((len = is.read(b)) != -1) {
                        fos.write(b, 0, len);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    public void create(ArrayList<File> sources, File destFile, File baseDir) throws IOException {

        // to avoid modifying input argument
        ArrayList<File> toAdd = new ArrayList<>(sources);
        ZipArchiveOutputStream zos = null;

        try {
            zos = new ZipArchiveOutputStream(new FileOutputStream(destFile));
            zos.setMethod(ZipArchiveOutputStream.DEFLATED);
            byte[] b = new byte[1024];

            //log.debug("Creating zip file: "+ficheroZip.getName());
            for (int i = 0; i < toAdd.size(); i++) {
                // note: cannot use foreach because sources gets modified
                File file = toAdd.get(i);

                // zip standard uses fw slashes instead of backslashes, always
                String baseName = baseDir.getAbsolutePath() + '/';
                String fileName = file.getAbsolutePath().substring(baseName.length());
                if (file.isDirectory()) {
                    fileName += '/';
                }
                ZipArchiveEntry entry = new ZipArchiveEntry(fileName);

                // skip directories - after assuring that their children *will* be included.
                if (file.isDirectory()) {
                    //log.debug("\tAdding dir "+fileName);
                    for (File child : file.listFiles()) {
                        toAdd.add(child);
                    }
                    zos.putArchiveEntry(entry);
                    continue;
                }

                //log.debug("\tAdding file "+fileName);

                // Add the zip entry and associated data.
                zos.putArchiveEntry(entry);

                int n;
                try (FileInputStream fis = new FileInputStream(file)) {
                    while ((n = fis.read(b)) > -1) {
                        zos.write(b, 0, n);
                    }
                    zos.closeArchiveEntry();
                }
            }
        } finally {
            if (zos != null) {
                zos.finish();
                zos.close();
            }
        }
    }

    /**
     * Simulates creation of a zip file, but returns only the size of the zip
     * that results from the given input stream
     */
    public int compressedSize(InputStream is) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ZipArchiveOutputStream zout = new ZipArchiveOutputStream(bos);) {
            zout.setMethod(ZipArchiveOutputStream.DEFLATED);
            ZipArchiveEntry entry = new ZipArchiveEntry("z");
            zout.putArchiveEntry(entry);
            int n;
            byte[] bytes = new byte[1024];
            while ((n = is.read(bytes)) > -1) {
                zout.write(bytes, 0, n);
            }
            is.close();
            zout.closeArchiveEntry();
            zout.finish();
            return bos.size();
        }
    }
}