diff options
Diffstat (limited to 'src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java')
-rw-r--r-- | src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java | 1862 |
1 files changed, 1862 insertions, 0 deletions
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java new file mode 100644 index 0000000..a3126fb --- /dev/null +++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java @@ -0,0 +1,1862 @@ +/* +* 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$ + } + } + } + +} |