package fr.itldev.koya.utils;
import fr.itldev.koya.exception.KoyaServiceException;
import fr.itldev.koya.model.exceptions.KoyaErrorCodes;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipError;
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.log4j.Logger;
import org.mozilla.universalchardet.UniversalDetector;
public class Zips {
static Logger logger = Logger.getLogger(Zips.class);
/**
* Unzips the specified zip file to the specified destination directory.
* Replaces any files in the destination, if they already exist.
*
* @param zipPath
* the name of the zip file to extract
* @param destPath
* the directory to unzip to
* @param defaultCharset
* @param failoverCharset
* @param sbLog
* In repository logger
*
* @return true if extract goes right
*/
public static boolean unzip(String zipPath, String destPath,
String defaultCharset, final String failoverCharset,
final StringBuffer sbLog) {
try {
final Path destDir = Paths.get(destPath);
// if the destination doesn't exist, create it
if (Files.notExists(destDir)) {
logger.trace(destDir + " does not exist. Creating...");
Files.createDirectories(destDir);
}
/* Define ZIP File System Properies in HashMap */
Map<String, String> zipProperties = new HashMap<>();
/* We want to read an existing ZIP File, so we set this to False */
zipProperties.put("create", "false");
String charset = determineCharset(zipPath);
if (charset != null && charset.toLowerCase().equals("windows-1252")) {
// ibm850 (winzip?), is detected as windows-1252)
charset = "ibm850";
} else if(charset == null ){
charset = defaultCharset;
}
final String finalCharset = charset;
zipProperties.put("encoding", finalCharset);
logger.debug(zipPath + " will be extracted using "
+ zipProperties.get("encoding"));
sbLog.append("\n[unzip] "+zipPath + " will be extracted using "
+ zipProperties.get("encoding"));
// convert the filename to a URI
final Path path = Paths.get(zipPath);
final URI uri = URI.create("jar:file:" + path.toUri().getPath());
try (FileSystem zipFileSystem = FileSystems.newFileSystem(uri,
zipProperties)) {
final Path root = zipFileSystem.getPath("/");
// walk the zip file tree and copy files to the destination
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
String filename = null;
try {
filename = file.toString();
} catch (IllegalArgumentException iae) {
logger.error(finalCharset + " failed using "
+ failoverCharset + " as last chance");
sbLog.append("\n[unzip] "+finalCharset + " failed using "
+ failoverCharset + " as last chance");
try {
filename = new String(getPathBytes(file),
failoverCharset);
} catch (IllegalAccessException ex) {
logger.error(ex.getMessage(), ex);
}
}
final Path destFile = Paths.get(destDir.toString(),
filename);
logger.trace("Extracting file " + filename + " to "
+ destFile);
sbLog.append("\n[unzip] Extracting file " + filename);
Files.copy(file, destFile,
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
final Path dirToCreate = Paths.get(destDir.toString(),
dir.toString());
if (Files.notExists(dirToCreate)) {
logger.trace("Creating directory " + dirToCreate);
sbLog.append("\n[unzip] Creating directory " + dirToCreate);
Files.createDirectory(dirToCreate);
}
return FileVisitResult.CONTINUE;
}
});
}
return true;
} catch (IOException ioe) {
logger.error(ioe.getMessage(), ioe);
return false;
} catch (ZipError ze) {
throw new KoyaServiceException(KoyaErrorCodes.INVALID_ZIP_ARCHIVE,
ze);
}
}
private static String determineCharset(String zipPath) throws IOException {
try (FileSystem zipFileSystem = FileSystems.newFileSystem(
Paths.get(zipPath), null)) {
final Path root = zipFileSystem.getPath("/");
final UniversalDetector detector = new UniversalDetector(null);
// walk the zip file tree to determine filename encoding
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
try {
handleData(file);
} catch (IllegalAccessException ex) {
logger.error(ex.getMessage(), ex);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
try {
handleData(dir);
} catch (IllegalAccessException ex) {
logger.error(ex.getMessage(), ex);
}
return FileVisitResult.CONTINUE;
}
private void handleData(Path p) throws IllegalAccessException {
if (p.getFileName() != null) {
byte[] b = getPathBytes(p.getFileName());
detector.handleData(b, 0, b.length);
}
}
});
detector.dataEnd();
String charsetDetectedName = detector.getDetectedCharset();
return charsetDetectedName;
}
}
private static byte[] getPathBytes(Path p) throws IllegalAccessException {
return (byte[]) FieldUtils.readDeclaredField(p, "path", true);
}
}