/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.common.utilities; import static com.motorola.studio.android.common.log.StudioLogger.warn; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.editors.text.TextFileDocumentProvider; import com.motorola.studio.android.common.CommonPlugin; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS; /** * DESCRIPTION: This class provides utility methods to handle files, like * copying and deleting directories sub-trees. * * USAGE: See public methods */ public class FileUtil { public static final int OS_WINDOWS = 0; public static final int OS_LINUX = 1; public static final char[] MAC_SPECIAL_CHAR = { '\\', ' ', '\'', '"', '!', '@', '$', '&', '*', '(', ')', '=', '`', '[', ']', '{', '}', '^', '<', '>', ':', ';', '?', '|' }; public static final char[] LINUX_SPECIAL_CHAR = { '\\', ' ', '\'', '"', '!', '$', '&', '*', '(', ')', '=', '`', '[', ']', '{', '}', '^', '<', '>', ':', ';', '?', '|' }; public static final char ESCAPE_CHAR = '\\'; private static final int BUFFER_SIZE = 1024; /** * Copy full list of contents from a directory to another. The source * directory is not created within the target one. * * @param fromDir * Source directory. * @param toDir * Target directory. * * @param IOException if I/O occurs */ public static void copyDir(File fromDir, File toDir) throws IOException { if ((fromDir != null) && fromDir.isDirectory() && fromDir.canRead() && (toDir != null) && toDir.isDirectory() && toDir.canWrite()) { for (File child : fromDir.listFiles()) { if (child.isFile()) { copyFile(child, new File(toDir, child.getName())); } else { // create directory and copy its children recursively File newDir = new File(toDir.getAbsolutePath(), child.getName()); newDir.mkdir(); copyDir(child, newDir); } } StudioLogger.info("The directory " + fromDir.getName() + " was successfully copied to " //$NON-NLS-1$ //$NON-NLS-2$ + toDir.getName() + "."); //$NON-NLS-1$ } else { //error detected String errorMessage = ""; //$NON-NLS-1$ if (fromDir == null) { errorMessage = "Null pointer for source directory."; //$NON-NLS-1$ } else { if (!fromDir.isDirectory()) { errorMessage = fromDir.getName() + " is not a directory."; //$NON-NLS-1$ } else { if (!fromDir.canRead()) { errorMessage = "Cannot read from " + fromDir.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$ } else { if (toDir == null) { errorMessage = "Null pointer for destination directory."; //$NON-NLS-1$ } else { if (!toDir.isDirectory()) { errorMessage = toDir.getName() + " is not a directory."; //$NON-NLS-1$ } else { if (!toDir.canWrite()) { errorMessage = "Cannot write to" + toDir.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$ } } } } } } StudioLogger.error(errorMessage); throw new IOException("Error copying directory: " + errorMessage); //$NON-NLS-1$ } } /** * Copies the source file to the given target. * * @param source - * the absolute path of the source file. * @param target - * the absolute path of the target file. */ public static void copyFile(File source, File target) throws IOException { copyFile(source.getAbsolutePath(), target.getAbsolutePath()); } /** * Copies the source file to the given target. * * @param source - * the absolute path of the source file. * @param target - * the absolute path of the target file. */ private static void copyFile(String source, String target) throws IOException { FileChannel sourceFileChannel = null; FileChannel targetFileChannel = null; FileInputStream sourceFileInStream = null; FileOutputStream targetFileOutStream = null; try { sourceFileInStream = new FileInputStream(source); sourceFileChannel = sourceFileInStream.getChannel(); targetFileOutStream = new FileOutputStream(target); targetFileChannel = targetFileOutStream.getChannel(); targetFileChannel.transferFrom(sourceFileChannel, 0, sourceFileChannel.size()); StudioLogger.info("The file " + source + " was successfully copied to " + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } catch (IOException e) { StudioLogger.error("Error copying file" + source + "to " + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ throw e; } finally { try { if (sourceFileChannel != null) { sourceFileChannel.close(); } } catch (IOException e) { StudioLogger.error("Error closing file " + source + "."); //$NON-NLS-1$ //$NON-NLS-2$ throw e; } try { if (targetFileChannel != null) { targetFileChannel.close(); } } catch (IOException e) { StudioLogger.error("Error closing file" + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ throw e; } try { if (sourceFileInStream != null) { sourceFileInStream.close(); } } catch (IOException e) { StudioLogger.error("Error closing file" + source + "."); //$NON-NLS-1$ //$NON-NLS-2$ throw e; } try { if (targetFileOutStream != null) { targetFileOutStream.close(); } } catch (IOException e) { StudioLogger.error("Error closing file" + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ throw e; } } } /** * This method deletes the directory, all files and all subdirectories under * it. If a deletion fails, the method stops attempting to delete and * returns false. * * @param directory * The directory to be deleted * @return Returns true if all deletions were successful. If the directory * doesn't exist returns false. * @throws IOException * When the parameter isn't a directory */ public static boolean deleteDirRecursively(File directory) throws IOException { String dirName = ""; //$NON-NLS-1$ boolean success = true; if (directory.exists()) { if (directory.isDirectory()) { dirName = directory.getName(); File[] children = directory.listFiles(); for (File element : children) { if (element.isFile()) { success = success && element.delete(); } else { success = success && deleteDirRecursively(element); } } success = success && directory.delete(); } else { String errorMessage = directory.getName() + " is not a diretory."; //$NON-NLS-1$ StudioLogger.error(errorMessage); throw new IOException(errorMessage); } } else { String errorMessage = "The directory does not exist."; //$NON-NLS-1$ StudioLogger.error(errorMessage); success = false; throw new IOException(errorMessage); } if ((success) && (!dirName.equals(""))) //$NON-NLS-1$ { StudioLogger.info("The directory " + dirName + "was successfully deleted."); //$NON-NLS-1$ //$NON-NLS-2$ } return success; } /** * Delete a single file from the filesystem. * * @param fileToDelete * A <code>File</code> object representing the file to be * deleted. * @throws IOException * if any problem occurs deleting the file. */ public static void deleteFile(File fileToDelete) throws IOException { if ((fileToDelete != null) && fileToDelete.exists() && fileToDelete.isFile() && fileToDelete.canWrite()) { fileToDelete.delete(); StudioLogger.info("The file " + fileToDelete.getName() + "was successfully deleted."); //$NON-NLS-1$ //$NON-NLS-2$ } else { String errorMessage = ""; //$NON-NLS-1$ if (fileToDelete == null) { errorMessage = "Null pointer for file to delete."; //$NON-NLS-1$ } else { if (!fileToDelete.exists()) { errorMessage = "The file " + fileToDelete.getName() + " does not exist."; //$NON-NLS-1$ //$NON-NLS-2$ } else { if (!fileToDelete.isFile()) { errorMessage = fileToDelete.getName() + " is not a file."; //$NON-NLS-1$ } else { if (!fileToDelete.canWrite()) { errorMessage = "Cannot write to " + fileToDelete.getName(); //$NON-NLS-1$ } } } } StudioLogger.error(errorMessage); throw new IOException("Cannot delete file: " + errorMessage); //$NON-NLS-1$ } } /** * Delete a list of files from the filesystem. * * @param filesToDelete * A list of <code>File</code> objects representing the files * to be deleted. * @throws IOException * if any problem occurs deleting the files. */ public static void deleteFilesOnList(List<File> filesToDelete) throws IOException { for (File element : filesToDelete) { if (element.exists()) { deleteFile((element)); } } } /** * Return the File Size in Bytes. * * @param root The root File, it can be a directory * @return The size of the file in bytes * @throws IOException */ public static int getFileSize(File root) throws IOException { int size = 0; if (root.isDirectory()) { for (File child : root.listFiles()) { size += FileUtil.getFileSize(child); } } else if (root.isFile()) { FileInputStream fis = new FileInputStream(root); int available; try { available = fis.available(); } finally { try { fis.close(); } catch (IOException e) { //Do thing. } } size = available; } return size; } /** * getExtension(String fileName) * * @param fileName * returns the extension of a given file. "extension" here means * the final part of the string after the last dot. * * @return String containing the extension */ public static String getExtension(String fileName) { if (fileName != null) { int i = fileName.lastIndexOf(".") + 1; //$NON-NLS-1$ return (i == 0) ? "" : fileName.substring(i); //$NON-NLS-1$ } else { StudioLogger.error("The file " + fileName + " does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ return null; } } /** * Get the list of all File objects that compose the path to the given File * object * * @param aFile * the file whose path must be retrieved. * @return a List with all the File objects that compose the path to the * given File object. */ public static List<File> getFilesComposingPath(File aFile) { List<File> fileList; if (aFile == null) { fileList = new ArrayList<File>(); } else { fileList = getFilesComposingPath(aFile.getParentFile()); fileList.add(aFile); } return fileList; } /** * Retrieve the relative filename to access a targetFile from a homeFile * parent directory. Notice that to actualy use a relative File object you * must use the following new File(homeDir, relativeFilename) because using * only new File(relativeFilename) would give you a file whose directory is * the one set in the "user.dir" property. * * @param homeDir * the directory from where you want to access the targetFile * @param targetFile * the absolute file or dir that you want to access via relative * filename from the homeFile * @return the relative filename that describes the location of the * targetFile referenced from the homeFile dir * @throws IOException */ public static String getRelativeFilename(File homeDir, File targetFile) throws IOException { StringBuffer relativePath = new StringBuffer(); List<File> homeDirList = getFilesComposingPath(getCanonicalFile(homeDir)); List<File> targetDirList = getFilesComposingPath(getCanonicalFile(targetFile)); if (homeDirList.size() == 0) { StudioLogger.info("Home Dir has no parent."); //$NON-NLS-1$ } if (targetDirList.size() == 0) { StudioLogger.info("Target Dir has no parent."); //$NON-NLS-1$ } // get the index of the last common directory between sourceFile and // targetFile int commonIndex = -1; for (int i = 0; (i < homeDirList.size()) && (i < targetDirList.size()); i++) { File aHomeDir = homeDirList.get(i); File aTargetDir = targetDirList.get(i); if (aHomeDir.equals(aTargetDir)) { commonIndex = i; } else { break; } } // return from all remaining directories of the homeFile for (int i = commonIndex + 1; i < homeDirList.size(); i++) { relativePath.append(".."); //$NON-NLS-1$ relativePath.append(File.separatorChar); } // enter into all directories of the target file // stops when reachs the file name and extension for (int i = commonIndex + 1; i < targetDirList.size(); i++) { File targetDir = targetDirList.get(i); relativePath.append(targetDir.getName()); if (i != (targetDirList.size() - 1)) { relativePath.append(File.separatorChar); } } return relativePath.toString(); } /** * Return a list of file absolute paths under "baseDir" and under its subdirectories, * recursively. * * @param baseDirToList * A string that represents the BaseDir to initial search. * @return A List of filepaths of files under the "baseDir". * @throws IOException * If the "baseDir" can not be read. */ public static List<String> listFilesRecursively(String baseDirToList) throws IOException { File baseDirToListFiles = new File(baseDirToList); List<String> listOfFiles = listFilesRecursively(baseDirToListFiles); return listOfFiles; } /** * Return a list of file absolute paths under "baseDir" and under its subdirectories, * recursively. * * @param baseDirToList * A file object that represents the "baseDir". * @return A List of filepaths of files under the "baseDir". * @throws IOException * If the "baseDir" can not be read. */ public static List<String> listFilesRecursively(File baseDirToList) throws IOException { List<String> listOfFiles = new ArrayList<String>(); if (baseDirToList.exists() && baseDirToList.isDirectory() && baseDirToList.canRead()) { File[] children = baseDirToList.listFiles(); for (File child : children) { if (child.isFile()) { listOfFiles.add(child.getAbsolutePath()); } else { List<String> temporaryList = listFilesRecursively(child); listOfFiles.addAll(temporaryList); } } } else { String errorMessage = ""; //$NON-NLS-1$ if (!baseDirToList.exists()) { errorMessage = "The base dir does not exist."; //$NON-NLS-1$ } else { if (!baseDirToList.isDirectory()) { errorMessage = baseDirToList.getName() + "is not a directory."; //$NON-NLS-1$ } else { if (!baseDirToList.canRead()) { errorMessage = "Cannot fread from " + baseDirToList.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$ } } } StudioLogger.error(errorMessage); throw new IOException("Error listing files: " + errorMessage); //$NON-NLS-1$ } return listOfFiles; } /** * Calculate the canonical (an absolute filename without "\.\" and "\..\") * that describe the file described by the absoluteFilename. * @param absoluteFilename a file name that describe the full path of the file to use. * @return the canonical File objecta */ public static File getCanonicalFile(String absoluteFilename) { return getCanonicalFile(new File(absoluteFilename)); } /** * Calculate the canonical (an absolute filename without "\.\" and "\..\") * that describe the file described by the given location and filename. * @param location the directory of the file to be used * @param filename (or a relative filename) of the file to be used * @return the canonical File objecta */ public static File getCanonicalFile(File location, String filename) { return getCanonicalFile(new File(location, filename)); } /** * Calculate the canonical (an absolute filename without "\.\" and "\..\") * that describe the given file. * @param aFile the file whose cannonical path will be calculated * @return the canonical File objecta */ public static File getCanonicalFile(File aFile) { File f = null; try { f = aFile.getCanonicalFile(); } catch (IOException e) { // this should never happens StudioLogger.error(FileUtil.class, "FileUtil.getCanonicalFile: IOException e", e); //$NON-NLS-1$ // since it's not possible to read from filesystem, return a File using String String filename = aFile.getAbsolutePath(); StringTokenizer st = new StringTokenizer(filename, File.separator); StringBuffer sb = new StringBuffer(); while (st.hasMoreTokens()) { String token = (String) st.nextElement(); if (token.equals("..")) //$NON-NLS-1$ { int lastDirIndex = sb.lastIndexOf(File.separator); // do not go back currently on the root directory if (lastDirIndex > 2) { sb.delete(lastDirIndex, sb.length()); } } else if (!token.equals(".")) //$NON-NLS-1$ { if (sb.length() > 0) { sb.append(File.separator); } sb.append(token); if (token.endsWith(":")) //$NON-NLS-1$ { sb.append(File.separator); } } } f = new File(sb.toString()); } return f; } /** * Returns which is the OS. * @return * a code corresponding to the proper OS */ public static int getOS() { int result = -1; String osName = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$ if (osName.indexOf("linux") > -1) //$NON-NLS-1$ { result = OS_LINUX; } else if (osName.indexOf("windows") > -1) //$NON-NLS-1$ { result = OS_WINDOWS; } return result; } /** * Returns true if the OS is windows * @return true if the OS is windows */ public static boolean isWindows() { return getOS() == OS_WINDOWS; } /** * Opens the stream; * * @param stream File Stream * * @return StringBuffer with the file content * * @throws IOException */ public static StringBuffer openFile(InputStream stream) throws IOException { InputStreamReader streamReader = null; StringBuffer fileBuffer = new StringBuffer(); BufferedReader reader = null; try { streamReader = new InputStreamReader(stream); reader = new BufferedReader(streamReader); char[] buffer = new char[1024]; int line = reader.read(buffer); while (line > 0) { fileBuffer.append(buffer, 0, line); line = reader.read(buffer); } } finally { if (streamReader != null) { try { streamReader.close(); } catch (Exception e) { //Do nothing. } } if (reader != null) { try { reader.close(); } catch (Exception e) { //Do nothing. } } } return fileBuffer; } /** * Reads a file into a string array * * @param filename The file name * @return The file contents as a string array * @throws IOException */ public static String[] readFileAsArray(String filename) throws IOException { LinkedList<String> file = new LinkedList<String>(); String[] lines = new String[0]; String line; FileReader reader = null; LineNumberReader lineReader = null; try { reader = new FileReader(filename); lineReader = new LineNumberReader(reader); while ((line = lineReader.readLine()) != null) { file.add(line); } lines = new String[file.size()]; lines = file.toArray(lines); } finally { try { lineReader.close(); reader.close(); } catch (Exception e) { // Do nothing } } return lines; } /** * Reads a file on workspace and returns an IDocument object with its content * * @param file The file to read * @return The IDocument object containing the file contents * * @throws CoreException */ public static IDocument readFile(IFile file) throws CoreException { if (!canRead(file)) { String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_TheFileCannotBeRead, file.getName()); IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg); throw new CoreException(status); } TextFileDocumentProvider documentProvider = new TextFileDocumentProvider(); IDocument document = new Document(); documentProvider.connect(file); document = documentProvider.getDocument(file); documentProvider.disconnect(file); return document; } /** * Saves the content of an IDocument object to a file on workspace * @param file The file * @param document The IDocument object * @param encoding The file encoding * @param overwrite If the file can be overwritten * @throws CoreException */ public static void saveFile(IFile file, IDocument document, String encoding, boolean overwrite) throws CoreException { if (file.exists() && !overwrite) { String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_CannotOverwriteTheFile, file.getName()); IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg); throw new CoreException(status); } if (!canWrite(file)) { String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_ErrorWritingTheFile, file.getName()); IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg); throw new CoreException(status); } ByteArrayInputStream bais = null; try { bais = new ByteArrayInputStream(document.get().getBytes(encoding)); file.setCharset(encoding, new NullProgressMonitor()); file.setContents(bais, true, false, new NullProgressMonitor()); } catch (UnsupportedEncodingException e1) { String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_ErrorSettingTheFileEncoding, file.getName()); IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg); throw new CoreException(status); } finally { if (bais != null) { try { bais.close(); } catch (IOException e) { // Do nothing. } } } } /** * Checks if a file can be read * * @param file The file to be checked * @return true if the file can be read or false otherwise */ public static boolean canRead(IFile file) { boolean canRead = true; InputStream is = null; try { if (file.exists()) { file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); is = file.getContents(); is.read(); } } catch (CoreException e) { canRead = false; } catch (IOException e) { canRead = false; } finally { if (is != null) { try { is.close(); } catch (IOException e) { // do nothing } } } return canRead; } /** * Checks if a file can be written * * @param file the file to be checked * @return true if the file can be written or false otherwise */ public static boolean canWrite(IFile file) { boolean canWrite = true; if (file.exists() && canRead(file)) { canWrite = !file.isReadOnly(); } else { IFolder parent = (IFolder) file.getParent(); if (!parent.isAccessible()) { canWrite = false; } else { try { if (parent.members() == null) { canWrite = false; } else { NullProgressMonitor nullProgressMonitor = new NullProgressMonitor(); file.create(null, true, nullProgressMonitor); file.refreshLocal(IResource.DEPTH_ZERO, nullProgressMonitor); file.delete(true, nullProgressMonitor); } } catch (CoreException e) { canWrite = false; } } } return canWrite; } /** * Checks if a File object can be read * * @param file the File object * * @return true if the File object can be read or false otherwise */ public static boolean canRead(File file) { boolean canRead = false; if ((file != null) && file.exists()) { FileInputStream fis = null; try { if (file.isFile()) { fis = new FileInputStream(file); fis.read(); canRead = true; } else { String[] children = file.list(); if (children != null) { canRead = true; } } } catch (Exception e) { // Do nothing. canRead is false already } finally { try { if (fis != null) { fis.close(); } } catch (IOException e) { // Do nothing } } } return canRead; } /** * Checks if a File object can be written * * @param file the File object * * @return true if the File object can be written or false otherwise */ public static boolean canWrite(File file) { boolean canWrite = false; if (file != null) { FileOutputStream fos = null; try { if (!file.exists()) { canWrite = file.createNewFile(); if (canWrite) { file.delete(); } } else if (file.isDirectory()) { File tempFile = File.createTempFile("StudioForAndroidFSChecking", null, file); //$NON-NLS-1$ if (tempFile.exists()) { canWrite = true; tempFile.delete(); } } else if (file.isFile()) { fos = new FileOutputStream(file); fos.getFD(); canWrite = true; } } catch (Exception e) { // Do nothing. canWrite is false already } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { // Do nothing } } } } return canWrite; } /** * Unpack a zip file. * * @param file the file * @param destination the destination path or null to unpack at the same directory of file * @return true if unpacked, false otherwise */ public static boolean unpackZipFile(File file, String destination, IProgressMonitor monitor) { SubMonitor subMonitor = SubMonitor.convert(monitor); ZipFile zipFile = null; String extractDestination = destination != null ? destination : file.getParent(); if (!extractDestination.endsWith(File.separator)) { extractDestination += File.separator; } boolean unziped = true; try { zipFile = new ZipFile(file); } catch (Throwable e) { unziped = false; StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() //$NON-NLS-1$ + " to " + extractDestination, e); //$NON-NLS-1$ } if (zipFile != null) { Enumeration<? extends ZipEntry> entries = zipFile.entries(); subMonitor.beginTask("Extracting files", Collections.list(entries).size()); //$NON-NLS-1$ entries = zipFile.entries(); InputStream input = null; OutputStream output = null; while (entries.hasMoreElements()) { try { ZipEntry entry = entries.nextElement(); File newFile = new File(extractDestination + entry.getName()); if (entry.isDirectory()) { newFile.mkdirs(); } else { newFile.getParentFile().mkdirs(); if (newFile.createNewFile()) { input = zipFile.getInputStream(entry); output = new BufferedOutputStream(new FileOutputStream(newFile)); copyStreams(input, output); } } } catch (Throwable t) { unziped = false; StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$ + extractDestination, t); } finally { try { if (input != null) { input.close(); } if (output != null) { output.close(); } } catch (Throwable t) { //do nothing } subMonitor.worked(1); } } } return unziped; } public static boolean extractZipArchive(File file, File destination, List<String> selectedEntries, IProgressMonitor monitor) throws IOException { SubMonitor subMonitor = SubMonitor.convert(monitor); ZipFile zipFile = null; CRC32 crc = new CRC32(); byte[] buf = new byte[BUFFER_SIZE]; File extractDestination = destination != null ? destination : file.getParentFile(); if (!extractDestination.exists()) { extractDestination.mkdirs(); } boolean unziped = true; try { zipFile = new ZipFile(file); } catch (Throwable e) { unziped = false; StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() //$NON-NLS-1$ + " to " + extractDestination, e); //$NON-NLS-1$ } if (zipFile != null) { Enumeration<? extends ZipEntry> entries = zipFile.entries(); subMonitor.beginTask("Extracting files", Collections.list(entries).size()); //$NON-NLS-1$ entries = zipFile.entries(); InputStream input = null; FileOutputStream output = null; int diagReturn = IDialogConstants.YES_ID; while (entries.hasMoreElements()) { crc.reset(); try { ZipEntry entry = entries.nextElement(); if (selectedEntries.contains(entry.getName())) { File newFile = new File(extractDestination, entry.getName()); if ((diagReturn != IDialogConstants.YES_TO_ALL_ID) && newFile.exists()) { diagReturn = EclipseUtils.showQuestionYesAllCancelDialog( UtilitiesNLS.FileUtil_File_Exists_Title, NLS.bind( UtilitiesNLS.FileUtil_File_Exists_Message, newFile.getAbsolutePath())); } if ((diagReturn == IDialogConstants.YES_ID) || (diagReturn == IDialogConstants.YES_TO_ALL_ID)) { newFile.delete(); if (entry.isDirectory()) { newFile.mkdirs(); } else { newFile.getParentFile().mkdirs(); if (newFile.createNewFile()) { input = zipFile.getInputStream(entry); output = new FileOutputStream(newFile); int length = 0; while ((length = input.read(buf, 0, BUFFER_SIZE)) > 1) { output.write(buf, 0, length); crc.update(buf, 0, length); } if (crc.getValue() != entry.getCrc()) { throw new IOException(); } } } } else { diagReturn = IDialogConstants.YES_ID; //Attempt to extract next entry } } } catch (IOException e) { unziped = false; StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$ + extractDestination, e); throw e; } catch (Throwable t) { unziped = false; StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$ + extractDestination, t); } finally { try { if (input != null) { input.close(); } if (output != null) { output.close(); } } catch (Throwable t) { //do nothing } subMonitor.worked(1); } } } return unziped; } /** * Unpack a tar file. * * @param file the file * @param destination the destination path or null to unpack at the same directory of file * @return true if unpacked, false otherwise */ public static boolean unpackTarFile(File artifactFile, String destination) { boolean unpacked = true; String extractDestination = destination != null ? destination : artifactFile.getParent(); if (!extractDestination.endsWith(File.separator)) { extractDestination += File.separator; } List<String> commandList = new LinkedList<String>(); commandList.add("tar"); //$NON-NLS-1$ String fileName = artifactFile.getName(); //tar.gz or tgz if (fileName.endsWith("gz")) //$NON-NLS-1$ { commandList.add("xzf"); //$NON-NLS-1$ } //tar.bz2 else if (fileName.endsWith("bz2")) //$NON-NLS-1$ { commandList.add("xjf"); //$NON-NLS-1$ } //tar else if (fileName.endsWith("tar")) //$NON-NLS-1$ { commandList.add("xf"); //$NON-NLS-1$ } else { unpacked = false; } if (unpacked) { commandList.add(artifactFile.getAbsolutePath()); File target = new File(extractDestination); if (target.exists() && target.isDirectory() && target.canWrite()) { try { Process p = Runtime.getRuntime().exec(commandList.toArray(new String[0]), null, target); try { p.waitFor(); } catch (InterruptedException e) { //do nothing } if (p.exitValue() != 0) { unpacked = false; } } catch (IOException e) { unpacked = false; } } } return unpacked; } /** * Copy the input stream to the output stream * @param inputStream * @param outputStream * @throws IOException */ public static void copyStreams(InputStream inputStream, OutputStream outputStream) throws IOException { byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) >= 0) { outputStream.write(buffer, 0, length); } } /** * Add a directory to a Project * @param project * @param parentFolder * @param folderName * @param monitor * @throws CoreException */ public static void createProjectFolder(IProject project, String parentFolder, String folderName, IProgressMonitor monitor) throws CoreException { monitor.beginTask(UtilitiesNLS.UI_Project_Creating_Folder_Task, 100); try { monitor.setTaskName(UtilitiesNLS.UI_Project_Verifying_Folder_Task); if (folderName.length() > 0) { monitor.worked(10); IFolder folder = project.getFolder(parentFolder + folderName); monitor.worked(10); if (!folder.exists()) { monitor.worked(10); if (FileUtil.canWrite(folder.getLocation().toFile())) { monitor.worked(10); monitor.setTaskName(UtilitiesNLS.UI_Project_Creating_Folder_Task); folder.create(true, true, new SubProgressMonitor(monitor, 60)); } else { String errMsg = NLS.bind( UtilitiesNLS.EXC_Project_CannotCreateFolderReadOnlyWorkspace, folder.getLocation().toFile().toString()); IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg); throw new CoreException(status); } } } } finally { monitor.done(); } } /** * Given a directory descriptor represented by a {@link File}, creates it * only if it does not exist. In case it does, try to create another * one with its name plus "-1". If it does exists, try to create it with * its name plus "+2", and so on... * <br> * Note that the directory is not fisically created. To do so, on must * use the method {@link File#mkdir()}. * * @param directory Directory to be created. * * @return Returns the created directory as a {@link File}. */ public static File createUniqueDirectoryDescriptor(File directory) { if (directory.exists()) { boolean exists = true; int counter = 1; String rootPath = directory.getAbsolutePath(); while (exists) { directory = new File(rootPath + "-" + counter); //$NON-NLS-1$ exists = directory.exists(); counter++; } } return directory; } /** * Return path with special characters escaped. * Special characters are system dependent, there is a set for linux and another for mac. * If {@code operationalSystem} is windows, the path is returned unchanged. * * @param path to be escaped. * @param operatingSystem the target operation system that the path will be used. * @return path with special characters escaped. * */ public static String getEscapedPath(String path, String operatingSystem) { char[] specialCharSet = null; if (operatingSystem.equals(Platform.OS_LINUX)) { specialCharSet = LINUX_SPECIAL_CHAR; } else if (operatingSystem.equals(Platform.OS_MACOSX)) { specialCharSet = MAC_SPECIAL_CHAR; } if ((path != null) && (specialCharSet != null)) { for (char c : specialCharSet) { CharSequence target = String.valueOf(c); CharSequence replacement = new String("\\" + String.valueOf(c)); //$NON-NLS-1$ path = path.replace(target, replacement); } } return path; } /** * Return path with special characters escaped. * Special characters are system dependent, there is a set for linux and another for mac. * If the system is windows, returns the path unchanged. * * @param path to be escaped * @return path with special characters escaped. * */ public static String getEscapedPath(String path) { return getEscapedPath(path, Platform.getOS()); } /** * Return path with special characters unescaped. * Special characters are system dependent, there is a set for linux and another for mac. * If the system is windows, returns the path unchanged. * * @param path to be unescaped * @return path with special characters unescaped. * */ public static String getUnescapedPath(String path) { char[] specialCharSet = null; if (Platform.getOS().equals(Platform.OS_LINUX)) { specialCharSet = LINUX_SPECIAL_CHAR; } else if (Platform.getOS().equals(Platform.OS_MACOSX)) { specialCharSet = MAC_SPECIAL_CHAR; } if ((path != null) && (specialCharSet != null)) { for (char c : specialCharSet) { CharSequence target = new String("\\") + String.valueOf(c); //$NON-NLS-1$ CharSequence replacement = String.valueOf(c); path = path.replace(target, replacement); } } return path; } public static String removeUnescapedQuotes(String path, String quoteReplacement) { //remove quotes and double quotes char quotes[] = { '\'', '"' }; boolean escaped = false; for (int i = 0; i < path.length(); i++) { if (escaped == false) { if (path.charAt(i) == ESCAPE_CHAR) { escaped = true; } else { for (char quote : quotes) { if (path.charAt(i) == quote) { //split the string in two parts: // - part1: before the quote String part1 = path.substring(0, i); // - part2: after the quote String part2 = path.substring(i + 1, path.length()); //concatenate part1 and part2 with quoteReplacement in-between //if quoteReplacement is the empty string (""), then part1 and part2 are juxtaposed path = part1.concat(quoteReplacement).concat(part2); } } } } else { //current character is escaped, next character can't be escaped escaped = false; } } return path; } /** * Unescape characters and remove quotes and double quotes. * Special characters are system dependent, there is a set for linux and another for mac. * If the system is windows, returns the path unchanged. * * @param path to be cleaned. * @param quoteReplacement string that will replace quotes and double quotes. * @return path without quotes, double quotes and special characters unescaped. * */ public static String getCleanPath(String path, String quoteReplacement) { path = removeUnescapedQuotes(path, quoteReplacement); path = getUnescapedPath(path); return path; } public static String calculateMd5Sum(File file) throws IOException { String md5Sum = null; BigInteger hash = null; FileInputStream fis; fis = new FileInputStream(file); byte[] buf = new byte[1500000]; try { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); //$NON-NLS-1$ int bytesRead = 0; while ((bytesRead = fis.read(buf)) > 0) { digest.update(buf, 0, bytesRead); } hash = new BigInteger(1, digest.digest()); md5Sum = hash.toString(16); } catch (NoSuchAlgorithmException e) { // This exception should not happen, because we are using a valid // hard // coded value for the algorithm name. However, if it happens, log // it. warn("MOTODEV Studio could not find an instance of the MessageDigest for the MD5 algorithm"); //$NON-NLS-1$ throw new IOException(UtilitiesNLS.FileUtil_Get_MD5_Algorithm_Failed); } finally { if (fis != null) { fis.close(); } } if (md5Sum == null) { throw new IOException(NLS.bind(UtilitiesNLS.FileUtil_MD5_Calculation_Failed, file.getAbsolutePath())); } return md5Sum; } /** * This method is responsible to copy informed source file to informed * target. * * @param sourceFile * @param targetFile * @throws IOException */ public static void copy(File sourceFile, File targetFile) throws IOException { OutputStream outputStream = new FileOutputStream(targetFile); InputStream inputStream = new FileInputStream(sourceFile); try { int length; byte[] buffer = new byte[FileUtil.BUFFER_SIZE]; while ((length = inputStream.read(buffer)) >= 0) { outputStream.write(buffer, 0, length); } } catch (IOException e) { throw new IOException("Error copying file:" + sourceFile.getAbsolutePath() + //$NON-NLS-1$ " to " + targetFile.getAbsolutePath()); //$NON-NLS-1$ } finally { outputStream.close(); inputStream.close(); } } /** * This method normalize a directory path. * * @param folder Full path to a directory * @return The normalized path. */ public static String normalizePath(String folder) { return folder.endsWith(File.separator) ? folder : folder + File.separator; } /** * Delete the specified file, recursively as necessary. * * @param file The file to delete */ public static void delete(File file) { if (file.exists()) { if (file.isDirectory()) { File[] files = file.listFiles(); for (int i = 0; i < files.length; i++) { delete(files[i]); } } file.delete(); } } /** * Delete the specified file, recursively as necessary. * * @param fileName The file to delete */ public static void delete(String fileName) { delete(new File(fileName)); } /** * This method creates the specified directory. * * @param directory The directory to create. * @throws IOException */ public static void mkdir(String directory) throws IOException { File f = new File(directory); if (f.exists()) { if (f.isFile()) { throw new IOException("Error creating directory:" + directory); //$NON-NLS-1$ } } else { if (!f.mkdirs()) { throw new IOException("Error creating directory:" + directory); //$NON-NLS-1$ } } } }