blob: 0ed95100958f67b59ecf375e01b7433cef5454e1 [file] [log] [blame]
/*
* Copyright (c) 2014 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @author Eike Stepper
*/
public final class ZIPUtil
{
/**
* @deprecated As of 3.4 use {@link #DEFAULT_BUFFER_SIZE}.
*/
@Deprecated
public static final int DEFALULT_BUFFER_SIZE = 4096;
public static final int DEFAULT_BUFFER_SIZE = DEFALULT_BUFFER_SIZE;
private static final int ORDER_KEEP = -1;
private static final int ORDER_SWAP = 1;
private ZIPUtil()
{
}
public static void zip(ZipEntryHandler handler, File zipFile) throws IORuntimeException
{
final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
final EntryContext context = new EntryContext();
FileOutputStream fos = IOUtil.openOutputStream(zipFile);
ZipOutputStream zos = null;
InputStream input = null;
ZipEntry entry = null;
try
{
zos = new ZipOutputStream(new BufferedOutputStream(fos, DEFAULT_BUFFER_SIZE));
for (;;)
{
handler.handleEntry(context);
if (context.isEmpty())
{
break;
}
try
{
String name = context.getName().replace(File.separatorChar, '/');
entry = new ZipEntry(name);
zos.putNextEntry(entry);
if (!context.isDirectory())
{
input = context.getInputStream();
if (input == null)
{
throw new IllegalStateException("Input is null for zip entry " + name); //$NON-NLS-1$
}
IOUtil.copy(input, zos, buffer);
}
}
finally
{
IOUtil.closeSilent(input);
if (entry != null)
{
zos.closeEntry();
}
context.reset();
}
}
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
finally
{
IOUtil.closeSilent(zos);
IOUtil.closeSilent(fos);
}
}
public static void zip(File sourceFolder, boolean excludeRoot, File zipFile)
{
zip(new FileSystemZipHandler(sourceFolder, excludeRoot), zipFile);
}
public static void unzip(File zipFile, UnzipHandler handler) throws IORuntimeException
{
FileInputStream fis = IOUtil.openInputStream(zipFile);
ZipInputStream zis = null;
try
{
zis = new ZipInputStream(new BufferedInputStream(fis, DEFAULT_BUFFER_SIZE));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null)
{
if (entry.isDirectory())
{
handler.unzipDirectory(entry.getName());
}
else
{
// TODO Provide delegating InputStream that ignores close()
handler.unzipFile(entry.getName(), zis);
}
}
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
finally
{
IOUtil.closeSilent(zis);
IOUtil.closeSilent(fis);
}
}
public static void unzip(File zipFile, File targetFolder) throws IORuntimeException
{
unzip(zipFile, new FileSystemUnzipHandler(targetFolder, DEFAULT_BUFFER_SIZE));
}
/**
* @author Eike Stepper
*/
public interface ZipEntryHandler
{
public void handleEntry(EntryContext context) throws IOException;
}
/**
* @author Eike Stepper
*/
public interface UnzipHandler
{
public void unzipDirectory(String name) throws IOException;
public void unzipFile(String name, InputStream zipStream) throws IOException;
}
/**
* @author Eike Stepper
*/
public static final class EntryContext
{
private static final String EMPTY = new String();
private String name = EMPTY;
private InputStream inputStream;
private boolean directory;
EntryContext()
{
}
void reset()
{
name = null;
inputStream = null;
}
boolean isEmpty()
{
return name == null;
}
boolean isDirectory()
{
return directory;
}
String getName()
{
return name;
}
InputStream getInputStream()
{
return inputStream;
}
public void setName(String name, boolean directory)
{
this.name = name + (directory ? "/" : ""); //$NON-NLS-1$ //$NON-NLS-2$
this.directory = directory;
}
public void setInputStream(InputStream inputStream)
{
this.inputStream = inputStream;
}
}
/**
* @author Eike Stepper
*/
public static class FileSystemZipHandler implements ZipEntryHandler
{
private int sourceFolderLength;
private transient Iterator<File> files;
public FileSystemZipHandler(File sourceFolder, boolean excludeRoot)
{
File root = excludeRoot ? sourceFolder : sourceFolder.getParentFile();
sourceFolderLength = root.getAbsolutePath().length();
if (excludeRoot)
{
++sourceFolderLength;
}
final int baseLength = sourceFolder.getAbsolutePath().length();
List<File> list = IOUtil.listBreadthFirst(sourceFolder);
Collections.sort(list, new Comparator<File>()
{
public int compare(File f1, File f2)
{
String path1 = getPath(f1, baseLength);
String path2 = getPath(f2, baseLength);
if (path1.length() == 0)
{
return ORDER_KEEP;
}
if (path2.length() == 0)
{
return ORDER_SWAP;
}
if (f1.isDirectory())
{
if (f2.isDirectory())
{
// f1=dir, f2=dir
if (path1.equalsIgnoreCase("/META-INF")) //$NON-NLS-1$
{
return ORDER_KEEP;
}
if (path2.equalsIgnoreCase("/META-INF")) //$NON-NLS-1$
{
return ORDER_SWAP;
}
return path1.compareTo(path2);
}
// f1=dir, f2=file
if (path1.equalsIgnoreCase("/META-INF")) //$NON-NLS-1$
{
return ORDER_KEEP;
}
if (path2.equalsIgnoreCase("/META-INF/MANIFEST.MF")) //$NON-NLS-1$
{
return ORDER_SWAP;
}
return ORDER_KEEP;
}
if (f2.isDirectory())
{
// f1=file, f2=dir
if (path2.equalsIgnoreCase("/META-INF")) //$NON-NLS-1$
{
return ORDER_SWAP;
}
if (path1.equalsIgnoreCase("/META-INF/MANIFEST.MF")) //$NON-NLS-1$
{
return ORDER_KEEP;
}
return ORDER_SWAP;
}
// f1=file, f2=file
if (path1.equalsIgnoreCase("/META-INF/MANIFEST.MF")) //$NON-NLS-1$
{
return ORDER_KEEP;
}
if (path2.equalsIgnoreCase("/META-INF/MANIFEST.MF")) //$NON-NLS-1$
{
return ORDER_SWAP;
}
return path1.compareTo(path2);
}
private String getPath(File file, int baseLength)
{
String absolutePath = file.getAbsolutePath();
String substring = absolutePath.substring(baseLength);
String replace = substring.replace(File.separatorChar, '/');
return replace;
}
});
files = list.iterator();
if (excludeRoot)
{
files.next();
}
}
public void handleEntry(EntryContext context) throws IOException
{
if (files.hasNext())
{
File file = files.next();
String name = getName(file);
if (name.length() != 0)
{
context.setName(name, file.isDirectory());
if (file.isFile())
{
context.setInputStream(IOUtil.openInputStream(file));
}
}
}
}
protected String getName(File file)
{
return file.getAbsolutePath().substring(sourceFolderLength);
}
}
/**
* @author Eike Stepper
*/
public static class FileSystemUnzipHandler implements UnzipHandler
{
private File targetFolder;
private transient byte[] buffer;
public FileSystemUnzipHandler(File targetFolder, int bufferSize)
{
this.targetFolder = targetFolder;
buffer = new byte[bufferSize];
}
public FileSystemUnzipHandler(File targetFolder)
{
this(targetFolder, DEFAULT_BUFFER_SIZE);
}
public File getTargetFolder()
{
return targetFolder;
}
public void unzipDirectory(String name)
{
File directory = new File(targetFolder, name);
if (!directory.exists())
{
directory.mkdirs();
}
}
public void unzipFile(String name, InputStream zipStream)
{
File targetFile = new File(targetFolder, name);
if (!targetFile.getParentFile().exists())
{
targetFile.getParentFile().mkdirs();
}
FileOutputStream out = IOUtil.openOutputStream(targetFile);
try
{
IOUtil.copy(zipStream, out, buffer);
}
finally
{
IOUtil.closeSilent(out);
}
}
}
}