001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.util;
020
021import icy.file.FileUtil;
022import icy.network.NetworkUtil;
023import icy.system.IcyExceptionHandler;
024
025import java.io.ByteArrayOutputStream;
026import java.io.IOException;
027import java.util.Enumeration;
028import java.util.zip.DataFormatException;
029import java.util.zip.Deflater;
030import java.util.zip.Inflater;
031import java.util.zip.ZipEntry;
032import java.util.zip.ZipFile;
033
034/**
035 * @author Stephane
036 */
037public class ZipUtil
038{
039    /**
040     * Compress the specified array of byte with given level of compression and return packed data.<br/>
041     * 
042     * @param packer
043     *        the packer object, can be <code>null</code> in which case we create a new Deflater object
044     * @param rawData
045     *        raw data to compress
046     * @param level
047     *        level of compression where 0 is low and 9 is high (use -1 to keep current / use default level)
048     */
049    public static byte[] pack(Deflater packer, byte[] rawData, int level)
050    {
051        final Deflater compressor;
052
053        if (packer != null)
054        {
055            compressor = packer;
056            compressor.reset();
057        }
058        else
059            compressor = new Deflater();
060
061        if (level != -1)
062            compressor.setLevel(level);
063
064        // give data to compress
065        compressor.setInput(rawData);
066        compressor.finish();
067
068        // create an expandable byte array to hold the compressed data.
069        final ByteArrayOutputStream bos = new ByteArrayOutputStream(rawData.length);
070        final byte[] buf = new byte[65536];
071
072        // pack data
073        while (!compressor.finished())
074        {
075            final int count = compressor.deflate(buf);
076
077            // nothing more to do ? --> end here
078            if (count == 0)
079                break;
080
081            bos.write(buf, 0, count);
082        }
083
084        try
085        {
086            bos.close();
087        }
088        catch (IOException e)
089        {
090            // we can freely ignore this one
091        }
092
093        // return packed data
094        return bos.toByteArray();
095    }
096
097    /**
098     * Compress the specified array of byte and return packed data
099     */
100    public static byte[] pack(byte[] rawData)
101    {
102        return pack(null, rawData, -1);
103    }
104
105    /**
106     * Uncompress the specified array of byte and return unpacked data
107     * 
108     * @param unpacker
109     *        the unpacker object, can be <code>null</code> in which case we create a new Inflater object
110     * @param packedData
111     *        packed data to uncompress
112     * @throws DataFormatException
113     */
114    public static byte[] unpack(Inflater unpacker, byte[] packedData) throws DataFormatException
115    {
116        final Inflater decompressor;
117
118        if (unpacker != null)
119        {
120            decompressor = unpacker;
121            decompressor.reset();
122        }
123        else
124            decompressor = new Inflater();
125
126        // give the data to uncompress
127        decompressor.setInput(packedData);
128
129        // create an expandable byte array to hold the uncompressed data
130        final ByteArrayOutputStream bos = new ByteArrayOutputStream(packedData.length);
131        final byte[] buf = new byte[65536];
132
133        // unpack data
134        while (!decompressor.finished())
135        {
136            final int count = decompressor.inflate(buf);
137
138            // nothing more to do ? --> end here
139            if (count == 0)
140                break;
141
142            bos.write(buf, 0, count);
143        }
144        try
145        {
146            bos.close();
147        }
148        catch (IOException e)
149        {
150            // we can freely ignore this one
151        }
152
153        // return unpacked data
154        return bos.toByteArray();
155    }
156
157    /**
158     * Uncompress the specified array of byte and return unpacked data
159     * 
160     * @throws DataFormatException
161     */
162    public static byte[] unpack(byte[] packedData) throws DataFormatException
163    {
164        return unpack(null, packedData);
165    }
166
167    /**
168     * Extract the specified zip file to the specified destination directory.
169     * 
170     * @param zipFile
171     *        input zip file name
172     * @param outputDirectory
173     *        output directory name
174     * @return true if file was correctly extracted, false otherwise
175     */
176    public static boolean extract(String zipFile, String outputDirectory)
177    {
178        boolean ok = true;
179
180        try
181        {
182            final ZipFile file = new ZipFile(zipFile);
183            final Enumeration<? extends ZipEntry> entries = file.entries();
184
185            while (entries.hasMoreElements())
186            {
187                ZipEntry entry = entries.nextElement();
188
189                if (entry.isDirectory())
190                {
191                    if (!FileUtil.createDir(outputDirectory + FileUtil.separator + entry.getName()))
192                    {
193                        System.err.println("ZipUtil.extract(" + zipFile + "," + outputDirectory + ") error :");
194                        System.err.println("Can't create directory : '" + outputDirectory + FileUtil.separator
195                                + entry.getName() + "'");
196                        ok = false;
197                        break;
198                    }
199                }
200                else if (!FileUtil.save(outputDirectory + FileUtil.separator + entry.getName(),
201                        NetworkUtil.download(file.getInputStream(entry)), true))
202                {
203                    System.err.println("ZipUtil.extract(" + zipFile + "," + outputDirectory + ") failed.");
204                    ok = false;
205                    break;
206                }
207            }
208
209            file.close();
210        }
211        catch (IOException ioe)
212        {
213            System.err.println("ZipUtil.extract(" + zipFile + "," + outputDirectory + ") error :");
214            IcyExceptionHandler.showErrorMessage(ioe, false);
215            ok = false;
216        }
217
218        return ok;
219    }
220
221    /**
222     * Extract the specified zip file in to default location.
223     * 
224     * @param zipFile
225     *        input zip file name
226     * @return true if file was correctly extracted, false otherwise
227     */
228    public static boolean extract(String zipFile)
229    {
230        return extract(zipFile, FileUtil.getDirectory(zipFile) + FileUtil.getFileName(zipFile, false));
231    }
232
233    /**
234     * Verify that specified file is a valid ZIP file
235     * 
236     * @param zipFile
237     *        input zip file name
238     * @throws IOException
239     *         if the input file is not a valid zip file.
240     */
241    public static void isValid(String zipFile) throws IOException
242    {
243        final ZipFile file = new ZipFile(zipFile);
244        try
245        {
246            final Enumeration<? extends ZipEntry> entries = file.entries();
247
248            while (entries.hasMoreElements())
249            {
250                ZipEntry entry = entries.nextElement();
251                entry.getName();
252            }
253        }
254        finally
255        {
256            file.close();
257        }
258    }
259
260    /**
261     * Verify that specified file is a valid ZIP file
262     * 
263     * @param zipFile
264     *        input zip file name
265     * @param showError
266     *        indicate if the method should show the error (in the output console) if the verify operation failed.
267     * @return true if the specified file is a valid ZIP file
268     */
269    public static boolean isValid(String zipFile, boolean showError)
270    {
271        try
272        {
273            isValid(zipFile);
274        }
275        catch (IOException e)
276        {
277            if (showError)
278            {
279                System.err.println("ZipUtil.isValid(" + zipFile + ") error :");
280                IcyExceptionHandler.showErrorMessage(e, false);
281            }
282
283            return false;
284        }
285
286        return true;
287    }
288}