package org.smoothbuild.builtin.compress;
import static java.io.File.createTempFile;
import static org.smoothbuild.io.fs.base.Path.path;
import static org.smoothbuild.util.Streams.copy;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.smoothbuild.io.fs.base.FileSystemException;
import org.smoothbuild.io.fs.base.IllegalPathException;
import org.smoothbuild.lang.message.ErrorMessage;
import org.smoothbuild.lang.plugin.Container;
import org.smoothbuild.lang.plugin.SmoothFunction;
import org.smoothbuild.lang.value.Array;
import org.smoothbuild.lang.value.ArrayBuilder;
import org.smoothbuild.lang.value.Blob;
import org.smoothbuild.lang.value.BlobBuilder;
import org.smoothbuild.lang.value.SFile;
import org.smoothbuild.lang.value.SString;
import org.smoothbuild.util.DuplicatesDetector;
public class UnzipFunction {
@SmoothFunction
public static Array<SFile> unzip(Container container, Blob blob) {
return unzip(container, blob, x -> true);
}
public static Array<SFile> unzip(Container container, Blob blob, Predicate<String> filter) {
DuplicatesDetector<String> duplicatesDetector = new DuplicatesDetector<>();
ArrayBuilder<SFile> fileArrayBuilder = container.create().arrayBuilder(SFile.class);
try {
File tempFile = copyToTempFile(blob);
try (ZipFile zipFile = new ZipFile(tempFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (!name.endsWith("/") && filter.test(name)) {
SFile unzippedEntry = unzipEntry(container, zipFile.getInputStream(entry), entry);
String fileName = unzippedEntry.path().value();
if (duplicatesDetector.addValue(fileName)) {
throw new ErrorMessage("archive contains two files with the same path = " + fileName);
}
fileArrayBuilder.add(unzippedEntry);
}
}
}
return fileArrayBuilder.build();
} catch (ZipException e) {
throw new ErrorMessage("Cannot read archive. Corrupted data?");
} catch (IOException e) {
throw new FileSystemException(e);
}
}
private static File copyToTempFile(Blob blob) throws IOException, FileNotFoundException {
File tempFile = createTempFile("unzip", null);
copy(blob.openInputStream(), new BufferedOutputStream(new FileOutputStream(tempFile)));
return tempFile;
}
private static SFile unzipEntry(Container container, InputStream inputStream, ZipEntry entry) {
String fileName = entry.getName();
try {
path(fileName);
} catch (IllegalPathException e) {
throw new ErrorMessage("File in archive has illegal name = '" + fileName + "'");
}
SString path = container.create().string(fileName);
Blob content = unzipEntryContent(container, inputStream);
return container.create().file(path, content);
}
private static Blob unzipEntryContent(Container container, InputStream inputStream) {
byte[] buffer = new byte[Constants.BUFFER_SIZE];
try {
BlobBuilder contentBuilder = container.create().blobBuilder();
try (OutputStream outputStream = contentBuilder) {
int len;
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
}
return contentBuilder.build();
} catch (IOException e) {
throw new FileSystemException(e);
}
}
}