com.motorola.studio.android.common.utilities.FileUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.motorola.studio.android.common.utilities.FileUtil.java

Source

/*
* 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$
            }
        }
    }

}