/*******************************************************************************
 * Copyright (c) 2012 GoPivotal, Inc.
 * 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:
 *     GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.core;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.springsource.ide.eclipse.commons.core.process.StandardProcessRunner;
import org.springsource.ide.eclipse.commons.core.util.OsUtils;


/**
 * @author Steffen Pingel
 * @author Leo Dos Santos
 * @author Christian Dupuis
 * @author Kris De Volder
 * @since 2.0
 */
public class ZipFileUtil {

	/**
	 * This Zip utility doesn't know how to extract file permission correctly
	 * from zip files (does Java ZipEntry even provide this info? It seems
	 * not!).
	 * <p>
	 * To remedy this problem, clients can provide their own logic for setting
	 * permissions they may care about. A subclass of PermissionSetter is used
	 * to determine the permission setting logic.
	 * 
	 * @author Kris De Volder
	 * 
	 * @since 2.8
	 */
	public static abstract class PermissionSetter {

		/**
		 * Called after a file was succesfully extracted from the zip archive.
		 * @throws IOException
		 */
		public abstract void fileUnzipped(ZipEntry entry, File entryFile) throws IOException;

		/**
		 * A permission setter that does nothing.
		 */
		public static final PermissionSetter NULL = new PermissionSetter() {
			@Override
			public void fileUnzipped(ZipEntry entry, File entryFile) {
				// Do nothing
			}
		};

		/**
		 * Creates an executable permission setter based on a list of file
		 * extensions.
		 * <p>
		 * Any file ending with the extension will be made executable.
		 */
		public static PermissionSetter executableExtensions(final String... exts) {
			if (OsUtils.isWindows()) {
				// It may be ok to do nothing for windows? (note: not tested!)
				return NULL;
			}
			else {
				// Assume we are unix if not Windows (Mac OS X is ok)
				return new PermissionSetter() {
					@Override
					public void fileUnzipped(ZipEntry entry, File entryFile) throws IOException {
						for (String ext : exts) {
							if (entryFile.getName().endsWith(ext)) {
								// This only works with Java 6:
								// entryFile.setExecutable(true);

								// This only works on Unix:
								StandardProcessRunner runner = new StandardProcessRunner();
								try {
									runner.run(new File("."), "chmod", "a+x", entryFile.toString());
								}
								catch (InterruptedException e) {
									// Restore the interrupted status (see
									// http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html)
									Thread.currentThread().interrupt();
								}
								return; // No sense making a file executable
										// more than once.
							}
						}
					}
				};
			}
		}

	}

	private static final int BUFFER_SIZE = 512 * 1024;

	public static void unzip(URL source, File targetFile, IProgressMonitor monitor) throws IOException {
		unzip(source, targetFile, null, monitor);
	}

	public static void unzip(URL source, File targetFile, String prefix, IProgressMonitor monitor) throws IOException {
		unzip(source, targetFile, prefix, PermissionSetter.NULL, monitor);
	}

	public static void unzip(URL source, File targetFile, String prefix, PermissionSetter permsetter,
			IProgressMonitor monitor) throws IOException {
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		try {
			monitor.beginTask("Extracting " + source.getFile(), IProgressMonitor.UNKNOWN);

			byte[] buffer = new byte[BUFFER_SIZE];
			ZipInputStream zipIn = new ZipInputStream(source.openStream());
			try {
				ZipEntry entry;
				while ((entry = zipIn.getNextEntry()) != null) {
					String name = entry.getName();
					if (prefix != null && name.startsWith(prefix)) {
						name = name.substring(prefix.length());
						if (name.length() > 1) {
							// cut off separator
							name = name.substring(1);
						}
					}

					Policy.checkCancelled(monitor);
					monitor.subTask(name);
					if (entry.isDirectory()) {
						new File(targetFile, name).mkdirs();
					}
					else {
						File entryFile = new File(targetFile, name);
						entryFile.getParentFile().mkdirs();
						FileOutputStream out = new FileOutputStream(entryFile);
						try {
							int len;
							while ((len = zipIn.read(buffer)) >= 0) {
								Policy.checkCancelled(monitor);
								out.write(buffer, 0, len);
							}
						}
						finally {
							out.close();
						}
						long modTime = entry.getTime();
						if (modTime > 0) {
							entryFile.setLastModified(modTime);
						}
						permsetter.fileUnzipped(entry, entryFile);
					}
				}
			}
			finally {
				zipIn.close();
			}
		}
		finally {
			monitor.done();
		}
	}

}

@@ -650,16 +650,55 @@
 eption;%0A
+import java.net.MalformedURLException;%0A
 import j
@@ -1552,25 +1552,24 @@
 g logic.%0A%09 *
- 
 %0A%09 * @author
@@ -1587,17 +1587,16 @@
 lder%0A%09 *
- 
 %0A%09 * @si
@@ -5302,10 +5302,192 @@
 %09%09%7D%0A%09%7D%0A%0A
+%09public static void unzip(File zipFile, File unzipDir, IProgressMonitor monitor) throws MalformedURLException, IOException %7B%0A%09%09unzip(zipFile.toURI().toURL(), unzipDir, monitor);%0A%09%7D%0A%0A
 %7D%0A

Add convenience method to ZipFileUtil