blob: 5aae2210b0ee775e9465409dfce8fefb7f4e2c42 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.releng.tests;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* A tool for performing operations on files.
*/
public class FileTool {
/**
* A zip filter which is used to filter out unwanted entries while extracting a zip file.
*
* @see FileTool#unzip
*/
public interface IZipFilter {
/**
* Returns a boolean indicating whether the entry with the
* specified name should be extracted from the zip file.
*
* @param fullEntryName the full entry name; includes full
* path segments for nested zip entries
* @param entryName the partial entry name; only includes
* path segments from the currect zip entry
* @param depth a number greater than or equal to zero
* which specifies the depth of the current nested zip
* entry
* @return a boolean indicating whether the entry with the
* specified name should be extracted from the zip file
*/
boolean shouldExtract(String fullEntryName, String entryName, int depth);
/**
* Returns a boolean indicating whether the entry (which
* is a zip/jar file) with the specified name should be
* extracted from the zip file and then unzipped.
*
* @param fullEntryName the full entry name; includes full
* path segments for nested zip entries
* @param entryName the partial entry name; only includes
* path segments from the currect zip entry
* @param depth a number greater than or equal to zero
* which specifies the depth of the current nested zip
* entry
* @return a boolean indicating whether the entry (which
* is a zip/jar file) with the specified name should be
* extracted from the zip file and then unzipped
*/
boolean shouldUnzip(String fullEntryName, String entryName, int depth);
}
/**
* Returns the given file path with its separator
* character changed from the given old separator to the
* given new separator.
*
* @param path a file path
* @param oldSeparator a path separator character
* @param newSeparator a path separator character
* @return the file path with its separator character
* changed from the given old separator to the given new
* separator
*/
public static String changeSeparator(String path, char oldSeparator, char newSeparator){
return path.replace(oldSeparator, newSeparator);
}
/**
* Unzips the given zip file to the given destination directory
* extracting only those entries the pass through the given
* filter.
*
* @param filter filters out unwanted zip entries
* @param zipFile the zip file to unzip
* @param dstDir the destination directory
*/
public static void unzip(IZipFilter filter, ZipFile zipFile, File dstDir) throws IOException {
unzip(filter, zipFile, dstDir, dstDir, 0);
}
private static void unzip(IZipFilter filter, ZipFile zipFile, File rootDstDir, File dstDir, int depth) throws IOException {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
try (zipFile) {
while(entries.hasMoreElements()){
ZipEntry entry = entries.nextElement();
if(entry.isDirectory()){
continue;
}
String entryName = entry.getName();
File file = new File(dstDir, FileTool.changeSeparator(entryName, '/', File.separatorChar));
String destCanPath = dstDir.getCanonicalPath();
String fileCanPath = file.getCanonicalPath();
if (!fileCanPath.startsWith(destCanPath + File.separatorChar)) {
throw new IOException("Entry is out side of target dir: " + entryName);
}
String fullEntryName = FileTool.changeSeparator(file.toString().substring(rootDstDir.toString().length() + 1), File.separatorChar, '/');
if(!(filter == null || filter.shouldExtract(fullEntryName, entryName, depth))){
continue;
}
file.getParentFile().mkdirs();
try (InputStream src = zipFile.getInputStream(entry);
OutputStream dst = new FileOutputStream(file)){
src.transferTo(dst);
}
if((entryName.endsWith(".zip") || entryName.endsWith(".jar")) && (filter == null || filter.shouldUnzip(fullEntryName, entryName, depth))) {
String fileName = file.getName();
String dirName = fileName.substring(0, fileName.length() - 4) + "_" + fileName.substring(fileName.length() - 3);
ZipFile innerZipFile = null;
try {
innerZipFile = new ZipFile(file);
File innerDstDir = new File(file.getParentFile(), dirName);
unzip(filter, innerZipFile, rootDstDir, innerDstDir, depth + 1);
file.delete();
} catch (IOException e) {
if(innerZipFile != null){
try {
innerZipFile.close();
System.out.println("Could not unzip: " + fileName + ". InnerZip = " + innerZipFile.getName() + ". Lenght: " + innerZipFile.getName().length());
} catch(IOException e2){
}
} else
System.out.println("Could not unzip: " + fileName + ". InnerZip = <null>");
e.printStackTrace();
}
}
}
}
}
/**
* Unzips the inner zip files in the given destination directory
* extracting only those entries the pass through the given
* filter.
*
* @param filter filters out unwanted zip entries
* @param dstDir the destination directory
*/
public static void unzip(IZipFilter filter, File dstDir) {
unzip(filter, dstDir, dstDir, 0);
}
private static void unzip(IZipFilter filter, File rootDstDir, File dstDir, int depth) {
File[] entries = rootDstDir.listFiles();
if (entries == null) {
entries = new File[0];
}
for (File entry : entries) {
if (entry.isDirectory()) {
unzip(filter, entry, dstDir, depth);
}
String entryName = entry.getName();
File file = new File(dstDir, FileTool.changeSeparator(entryName, '/', File.separatorChar));
if (entryName.endsWith(".zip") || entryName.endsWith(".jar")) {
String fileName = file.getName();
String dirName = fileName.substring(0, fileName.length() - 4) + "_"
+ fileName.substring(fileName.length() - 3);
ZipFile innerZipFile = null;
try {
innerZipFile = new ZipFile(entry);
File innerDstDir = new File(entry.getParentFile(), dirName);
unzip(filter, innerZipFile, rootDstDir, innerDstDir, depth + 1);
// entry.delete();
} catch (IOException e) {
if (innerZipFile != null) {
try {
innerZipFile.close();
System.out.println("Could not unzip: " + fileName + ". InnerZip = " + innerZipFile.getName()
+ ". Lenght: " + innerZipFile.getName().length());
} catch (IOException e2) {
}
} else
System.out.println("Could not unzip: " + fileName + ". InnerZip = <null>");
e.printStackTrace();
}
}
}
}
}