/**
* Copyright (c) 2002-2007 IBM Corporation and others.
* 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:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.test.build;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* 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(IZipFilter, ZipFile, File, File, int)
*/
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
*/
public 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
*/
public boolean shouldUnzip(String fullEntryName, String entryName, int depth);
}
/**
* A buffer.
*/
private static byte[] buffer = new byte [8192];
/**
* 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);
}
/**
* Returns a boolean indicating whether the given files have the same
* content.
*
* @param file1
* the first file
* @param file2
* the second file
* @return a boolean indicating whether the given files have the same
* content
*/
public static boolean compare(File file1, File file2) throws IOException
{
if (file1.length() != file2.length())
{
return false;
}
InputStream is1 = null;
InputStream is2 = null;
try
{
is1 = new BufferedInputStream(new FileInputStream(file1));
is2 = new BufferedInputStream(new FileInputStream(file2));
int a = 0;
int b = 0;
boolean same = true;
while (same && a != -1 && b != -1)
{
a = is1.read();
b = is2.read();
same = a == b;
}
return same;
}
finally
{
if (is2 != null)
{
try
{
is2.close();
}
catch (IOException e)
{
// Ignore
}
}
if (is1 != null)
{
try
{
is1.close();
}
catch (IOException e)
{
// Ignore
}
}
}
}
/**
* Copies the given source file to the given destination file.
*
* @param src
* the given source file
* @param dst
* the given destination file
*/
public static void copy(File src, File dst) throws IOException
{
copy(src.getParentFile(), src, dst);
}
/**
* Copies the given source file to the given destination file.
*
* @param root
* @param src
* the given source file
* @param dst
* the given destination file
*/
public static void copy(File root, File src, File dst) throws IOException
{
if (src.isDirectory())
{
String[] children = src.list();
for (int i = 0; i < children.length; ++i)
{
File child = new File(src, children[i]);
copy(root, child, dst);
}
}
else
{
String rootString = root.toString();
String srcString = src.toString();
File dstFile = new File(dst, srcString.substring(rootString.length() + 1));
transferData(src, dstFile);
}
}
/**
* Delete the given file or directory. Directories are deleted recursively.
* If the file or directory can not be deleted, a warning message is
* written to stdout.
*
* @param file
* a file or directory
*/
public static void delete(File file)
{
if (file.exists())
{
if (file.isDirectory())
{
String[] children = file.list();
for (int i = 0; i < children.length; ++i)
{
File child = new File(file, children[i]);
delete(child);
}
}
if (!file.delete())
{
System.out.println("WARNING: could not delete " + file);
}
}
}
/**
* Returns a new <code>File</code> from the given path name segments.
*
* @param segments
* the given path name segments
* @return a new <code>File</code> from the given path name segments
*/
public static File getFile(String[] segments)
{
File result = new File(segments[0]);
for (int i = 1; i < segments.length; ++i)
{
result = new File(result, segments[i]);
}
return result;
}
/**
* Returns a list of all files below the given directory that end with a
* string in the given include list and do not end with a string in the
* given exclude list. If include is <code>null</code> all files are
* included except those that are explicitly excluded. If exclude is <code>null</code>
* no files are excluded except those that are not included.
*
* @param dir
* the given directory
* @param include
* a list of filenames to include
* @param exclude
* a list of filenames to exclude
* @return a list of all files below the given directory that are included
* and not explicitly excluded
*/
public static File[] getFiles(File dir, String[] include, String[] exclude)
{
List<File> list = new ArrayList<File>();
String[] children = dir.list();
if (children == null)
{
return new File [0];
}
for (int i = 0; i < children.length; ++i)
{
File child = new File(dir, children[i]);
String name = child.getName();
if (child.isDirectory())
{
File[] result = getFiles(child, include, exclude);
for (int j = 0; j < result.length; ++j)
{
list.add(result[j]);
}
}
else
{
boolean includeFile = include == null;
if (include != null)
{
for (int j = 0; j < include.length; ++j)
{
if (name.endsWith(include[j]))
{
includeFile = true;
break;
}
}
}
boolean excludeFile = exclude != null;
if (exclude != null)
{
for (int j = 0; j < exclude.length; ++j)
{
if (name.endsWith(exclude[j]))
{
excludeFile = true;
break;
}
}
}
if (includeFile && !excludeFile)
{
list.add(child);
}
}
}
return list.toArray(new File [0]);
}
/**
* Breaks the given file into its path name segments and returns the
* result.
*
* @param file
* a file or directory
* @return the path name segments of the given file
*/
public static String[] getSegments(File file)
{
return getSegments(file.toString(), File.separatorChar);
}
/**
* Breaks the given string into segments and returns the result.
*
* @param s
* a string
* @param separator
* the segment separator
* @return the segments of the given string
*/
public static String[] getSegments(String s, char separator)
{
List<String> result = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(s, "" + separator);
while (tokenizer.hasMoreTokens())
{
result.add(tokenizer.nextToken());
}
return result.toArray(new String [0]);
}
/**
* Returns a vector of <code>File</code> paths parsed from the given
* paths string.
*
* @param paths
* a paths string
* @return a vector of <code>File</code> paths parsed from the given
* paths string
*/
public static File[] parsePaths(String paths)
{
List<File> result = new ArrayList<File>();
StringTokenizer tokenizer = new StringTokenizer(paths, ";");
while (tokenizer.hasMoreTokens())
{
result.add(new File(tokenizer.nextToken()));
}
return result.toArray(new File [0]);
}
/**
* Copies all bytes in the given source file to the given destination file.
*
* @param source
* the given source file
* @param destination
* the given destination file
*/
public static void transferData(File source, File destination) throws IOException
{
destination.getParentFile().mkdirs();
InputStream is = null;
OutputStream os = null;
try
{
is = new FileInputStream(source);
os = new FileOutputStream(destination);
transferData(is, os);
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e)
{
// Ignore
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
// Ignore
}
}
}
}
/**
* Copies all bytes in the given source stream to the given destination
* stream. Neither streams are closed.
*
* @param source
* the given source stream
* @param destination
* the given destination stream
*/
public static void transferData(InputStream source, OutputStream destination) throws IOException
{
int bytesRead = 0;
while (bytesRead != -1)
{
bytesRead = source.read(buffer, 0, buffer.length);
if (bytesRead != -1)
{
destination.write(buffer, 0, bytesRead);
}
}
}
/**
* 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
{
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 fullEntryName = FileTool.changeSeparator(file.toString().substring(rootDstDir.toString().length() + 1), File.separatorChar,
'/');
if (!(filter == null || filter.shouldExtract(fullEntryName, entryName, depth)))
{
continue;
}
file.getParentFile().mkdirs();
InputStream src = null;
OutputStream dst = null;
try
{
src = zipFile.getInputStream(entry);
dst = new FileOutputStream(file);
transferData(src, dst);
}
finally
{
if (dst != null)
{
try
{
dst.close();
}
catch (IOException e)
{
// Ignore
}
}
if (src != null)
{
try
{
src.close();
}
catch (IOException e)
{
// Ignore
}
}
}
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();
}
catch (IOException e2)
{
// Ignore
}
System.out.println("Could not unzip: " + fileName + ". InnerZip = " + innerZipFile.getName() + ". Length: "
+ innerZipFile.getName().length());
}
e.printStackTrace();
}
}
}
}
finally
{
try
{
zipFile.close();
}
catch (IOException e)
{
// Ignore
}
}
}
/**
* Zips the given directory to the given zip file. Directories are zipped
* recursively. Inner zip files are created for directories that end with
* "_zip" or "_jar". If verbose is true, progress information is logged.
*
* @param dir the directory to zip
* @param zipFile the resulting zip file
*/
public static void zip(File dir, File zipFile) throws IOException
{
BufferedOutputStream bos = null;
ZipOutputStream zos = null;
try
{
bos = new BufferedOutputStream(new FileOutputStream(zipFile));
zos = new ZipOutputStream(bos);
zip(dir, dir, zos);
}
finally
{
if (zos == null)
{
if (bos != null)
{
try
{
bos.close();
}
catch (IOException e)
{
// Ignore
}
}
}
else
{
try
{
zos.close();
}
catch (IOException e)
{
// Ignore
}
}
}
}
private static void zip(File root, File file, ZipOutputStream zos) throws IOException
{
if (file.isDirectory())
{
String name = file.getName();
if (name.endsWith("_zip") || name.endsWith("_jar"))
{
String rootString = root.toString();
String fileString = file.toString();
String zipEntryName = fileString.substring(rootString.length() + 1);
int underscoreIndex = zipEntryName.lastIndexOf("_");
zipEntryName = zipEntryName.substring(0, underscoreIndex) + "." + zipEntryName.substring(underscoreIndex + 1);
ZipEntry zipEntry = new ZipEntry(changeSeparator(zipEntryName, File.separatorChar, '/'));
zos.putNextEntry(zipEntry);
ZipOutputStream zos2 = new ZipOutputStream(zos);
String[] list = file.list();
for (int i = 0; i < list.length; ++i)
{
File item = new File(file, list[i]);
zip(file, item, zos2);
}
zos2.finish();
zos.closeEntry();
}
else
{
String[] list = file.list();
for (int i = 0; i < list.length; ++i)
{
File item = new File(file, list[i]);
zip(root, item, zos);
}
}
}
else
{
String rootString = root.toString();
String fileString = file.toString();
String zipEntryName = fileString.substring(rootString.length() + 1);
ZipEntry zipEntry = new ZipEntry(changeSeparator(zipEntryName, File.separatorChar, '/'));
zos.putNextEntry(zipEntry);
FileInputStream fos = null;
try
{
fos = new FileInputStream(file);
transferData(fos, zos);
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (IOException e)
{
// Ignore
}
}
}
zos.closeEntry();
}
}
}