package com.intellij.platform.templates.github;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.Producer;
import com.intellij.util.io.DownloadUtil;
import com.intellij.util.io.Outcome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author Sergey Simonchik
*/
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
public class ZipUtil {
private static final Logger LOG = Logger.getInstance(ZipUtil.class);
public interface ContentProcessor {
/** Return null to skip the file */
@Nullable
byte[] processContent(byte[] content, File file) throws IOException;
}
public static void unzipWithProgressSynchronously(
@Nullable Project project,
@NotNull String progressTitle,
@NotNull final File zipArchive,
@NotNull final File extractToDir) throws GeneratorException
{
Outcome<Boolean> outcome = DownloadUtil.provideDataWithProgressSynchronously(
project, progressTitle, "Unpacking ...",
new Callable<Boolean>() {
@Override
public Boolean call() throws IOException {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
ZipInputStream stream = new ZipInputStream(new FileInputStream(zipArchive));
unzip(progress, extractToDir, stream, null, null);
return true;
}
},
new Producer<Boolean>() {
@Override
public Boolean produce() {
return false;
}
}
);
Boolean result = outcome.get();
if (result == null) {
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
Exception e = outcome.getException();
if (e != null) {
throw new GeneratorException("Unpacking failed, downloaded archive is broken");
}
throw new GeneratorException("Unpacking was cancelled");
}
}
public static void unzip(@Nullable ProgressIndicator progress,
File extractToDir,
ZipInputStream stream,
@Nullable NullableFunction<String, String> pathConvertor,
@Nullable ContentProcessor contentProcessor) throws IOException {
if (progress != null) {
progress.setText("Extracting...");
}
try {
ZipEntry entry;
while ((entry = stream.getNextEntry()) != null) {
unzipEntryToDir(progress, entry, extractToDir, stream, pathConvertor, contentProcessor);
}
} finally {
stream.close();
}
}
private static void unzipEntryToDir(@Nullable ProgressIndicator progress,
@NotNull final ZipEntry zipEntry,
@NotNull final File extractToDir,
ZipInputStream stream,
@Nullable NullableFunction<String, String> pathConvertor,
@Nullable ContentProcessor contentProcessor) throws IOException {
String relativeExtractPath = createRelativeExtractPath(zipEntry);
if (pathConvertor != null) {
relativeExtractPath = pathConvertor.fun(relativeExtractPath);
if (relativeExtractPath == null) {
// should be skipped
return;
}
}
File child = new File(extractToDir, relativeExtractPath);
File dir = zipEntry.isDirectory() ? child : child.getParentFile();
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("Unable to create dir: '" + dir + "'!");
}
if (zipEntry.isDirectory()) {
return;
}
if (progress != null) {
progress.setText("Extracting " + relativeExtractPath + " ...");
}
FileOutputStream fileOutputStream = null;
try {
if (contentProcessor == null) {
fileOutputStream = new FileOutputStream(child);
FileUtil.copy(stream, fileOutputStream);
}
else {
byte[] content = contentProcessor.processContent(FileUtil.loadBytes(stream), child);
if (content != null) {
fileOutputStream = new FileOutputStream(child);
fileOutputStream.write(content);
}
}
} finally {
if (fileOutputStream != null) {
fileOutputStream.close();
}
}
LOG.info("Extract: " + relativeExtractPath);
}
private static String createRelativeExtractPath(ZipEntry zipEntry) {
String name = zipEntry.getName();
int ind = name.indexOf('/');
if (ind >= 0) {
name = name.substring(ind + 1);
}
return StringUtil.trimEnd(name, "/");
}
}