com.google.dart.tools.core.utilities.io.FileUtilities.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.core.utilities.io.FileUtilities.java

Source

/*
 * Copyright (c) 2012, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
 * 
 * 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.google.dart.tools.core.utilities.io;

import com.google.common.io.CharStreams;
import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.utilities.logging.Logger;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.URL;

/**
 * The class <code>FileUtilities</code> implements utility methods used to create and manipulate
 * files.
 * 
 * @coverage dart.tools.core.utilities
 */
public class FileUtilities {
    /**
     * Copy over all the files and directories contained in the source directory to the target
     * directory.
     * 
     * @param sourceDirectory the directory whose contents are to be copied
     * @param targetDirectory the directory to which the contents are to be copied
     */
    public static void copyDirectoryContents(File sourceDirectory, File targetDirectory) throws IOException {
        File[] children;
        File sourceFile, targetFile;

        if (!sourceDirectory.exists()) {
            throw new IllegalArgumentException(
                    "sourceDirectory does not exist: " + sourceDirectory.getAbsolutePath()); //$NON-NLS-1$
        } else if (!sourceDirectory.isDirectory()) {
            throw new IllegalArgumentException(
                    "sourceDirectory is not a directory: " + sourceDirectory.getAbsolutePath()); //$NON-NLS-1$
        }
        if (!targetDirectory.exists()) {
            targetDirectory.mkdirs();
        } else if (!targetDirectory.isDirectory()) {
            throw new IllegalArgumentException(
                    "targetDirectory is not a directory: " + targetDirectory.getAbsolutePath()); //$NON-NLS-1$
        }
        children = sourceDirectory.listFiles();
        for (int i = 0; i < children.length; i++) {
            sourceFile = children[i];
            targetFile = new File(targetDirectory, sourceFile.getName());
            if (sourceFile.isDirectory()) {
                copyDirectoryContents(sourceFile, targetFile);
            } else {
                copyFile(sourceFile, targetFile);
            }
        }
    }

    /**
     * Copy the contents of the given input file to the given output file.
     * 
     * @param input the input file from which the contents are to be read
     * @param output the output file to which the contents are to be written
     * @throws IOException if the files cannot be either read or written
     */
    public static void copyFile(File input, File output) throws IOException {
        copyFile(new FileInputStream(input), new FileOutputStream(output));
    }

    /**
     * Copy all of the bytes from the given input stream to the given output stream. Both streams will
     * be closed after the operation.
     * 
     * @param input the input stream from which bytes are to be read
     * @param output the output stream to which bytes are to be written
     * @throws IOException if the bytes cannot be either read or written
     */
    public static void copyFile(InputStream input, OutputStream output) throws IOException {
        int bufferSize, readSize;
        byte[] buffer;

        bufferSize = 8192;
        buffer = new byte[bufferSize];
        try {
            readSize = input.read(buffer, 0, bufferSize);
            while (readSize >= 0) {
                output.write(buffer, 0, readSize);
                readSize = input.read(buffer, 0, bufferSize);
            }
        } finally {
            try {
                input.close();
            } catch (IOException exception) {
                // ignore failures to close the input
            }
            try {
                output.close();
            } catch (IOException exception) {
                // ignore failures to close the output
            }
        }
    }

    /**
     * Copy the contents of the given input file to the given output file.
     * 
     * @param input the input file from which the contents are to be read
     * @param output the output file to which the contents are to be written
     * @throws IOException if the files cannot be either read or written
     */
    public static void copyFile(URL input, File output) throws IOException {
        copyFile(input.openStream(), new FileOutputStream(output));
    }

    /**
     * Create the given file, including any parent directories that do not already exist.
     * 
     * @param file the file to be created
     * @throws IOException if the file could not be created
     */
    public static void create(File file) throws IOException {
        File parent;

        parent = file.getParentFile();
        if (parent != null && !parent.exists()) {
            if (!parent.mkdirs()) {
                throw new IOException("Could not create directory " + parent.getAbsolutePath()); //$NON-NLS-1$
            }
        }
        if (file.isDirectory()) {
            if (!file.mkdir()) {
                throw new IOException("Could not create directory " + file.getAbsolutePath()); //$NON-NLS-1$
            }
        } else {
            file.createNewFile();
        }
    }

    /**
     * Given some {@link File} valid absolute directory path on disk that does or doesn't exist yet,
     * this method will create the directories no disk that don't yet exist.
     * 
     * @param directory some valid absolute directory path on disk that does or doesn't exist yet
     */
    public static void createDirectory(File directory) {
        if (directory == null) {
            return;
        } else if (directory.isDirectory()) {
            return;
        }
        createDirectory(directory.getParentFile());
        directory.mkdir();
    }

    /**
     * Delete the given file or directory. If the argument is a directory, then the contents of the
     * directory will be deleted before the directory itself is deleted.
     * 
     * @param file the file or directory to be deleted
     */
    public static void delete(File file) {
        if (file.isDirectory()) {
            safelyDeleteContents(file);
        }
        file.delete();
    }

    /**
     * Delete the contents of the given directory without deleting the directory itself.
     * 
     * @param directory the directory whose contents are to be deleted
     * @throws IllegalArgumentException if the argument is not a directory
     */
    public static void deleteContents(File directory) {
        if (!directory.isDirectory()) {
            throw new IllegalArgumentException(
                    "Cannot delete file contents: \"" + directory.getAbsolutePath() + "\""); //$NON-NLS-1$
        }
        safelyDeleteContents(directory);
    }

    /**
     * Return a string that contains all of the characters in the given string that can be included in
     * a valid file name.
     * 
     * @param string the string used to build the valid file name
     * @return a valid file name that is as close to the given string as possible
     */
    public static String deriveFileName(String string) {
        if (string == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        int length = string.length();
        for (int i = 0; i < length; i++) {
            char currentChar = string.charAt(i);
            if (isValidFileNameChar(currentChar)) {
                builder.append(currentChar);
            }
        }
        return builder.toString();
    }

    /**
     * Ensure that the given file exists and is executable. If it exists but is not executable, then
     * make it executable and log that it was necessary for us to do so.
     * <p>
     * Originally copied from the engine project
     * 
     * @return {@code true} if the file exists and is executable, else {@code false}.
     */
    public static boolean ensureExecutable(File file) {
        if (file == null || !file.exists()) {
            return false;
        }
        if (!file.canExecute()) {
            Logger logger = AnalysisEngine.getInstance().getLogger();
            if (!makeExecutable(file)) {
                logger.logError(file + " cannot be made executable");
                return false;
            }
            logger.logError(file + " was not executable");
        }
        return true;
    }

    /**
     * Return the base name of the given file. The base name is the portion of the name that occurs
     * before the period or extension when a file name is assumed to be of the form
     * <code>baseName '.' extension</code>.
     * <p>
     * Originally copied from the engine project
     * 
     * @return the base name of the given file
     */
    public static String getBaseName(File file) {
        String name;
        int index;

        name = file.getName();
        index = name.lastIndexOf('.');
        if (index >= 0) {
            return name.substring(0, index);
        }
        return name;
    }

    /**
     * Return the contents of the given file, interpreted as a string.
     * 
     * @param file the file whose contents are to be returned
     * @return the contents of the given file, interpreted as a string
     * @throws IOException if the file contents could not be read
     */
    public static String getContents(File file, String charsetName) throws IOException {
        Reader streamReader = new InputStreamReader(new FileInputStream(file), charsetName);
        BufferedReader reader = new BufferedReader(streamReader);
        return getContents(reader);
    }

    /**
     * Return the contents of the given reader, interpreted as a string. The {@link Reader} will be
     * closed.
     * 
     * @param reader the reader whose contents are to be returned
     * @return the contents of the given reader, interpreted as a string
     * @throws IOException if the reader could not be read
     */
    public static String getContents(Reader reader) throws IOException {
        try {
            return CharStreams.toString(reader);
        } finally {
            reader.close();
        }
    }

    /**
     * @return the contents of the given Dart file with UTF-8 encoding.
     */
    public static String getDartContents(File file) throws IOException {
        return getContents(file, "UTF-8");
    }

    /**
     * Return the extension of the given file. The extension is the portion of the name that occurs
     * after the final period when a file name is assumed to be of the form
     * <code>baseName '.' extension</code>.
     * 
     * @return the extension of the given file
     */
    public static String getExtension(File file) {
        String name;
        int index;

        name = file.getName();
        index = name.lastIndexOf('.');
        if (index >= 0) {
            return name.substring(index + 1);
        }
        return "";
    }

    /**
     * If the file with given path exists, and its canonical path differs from the given path only in
     * case, return the path with the same case as the {@link File} in the file system.
     * <p>
     * According to specification it is OK to use a different case in URI, as long as embedding allows
     * this. But Eclipse LTK (refactoring framework) is not happy. We need to convert such URI to
     * canonical, at least during refactoring.
     */
    public static String getFileSystemCase(String path) {
        File file = new File(path);
        if (file.exists()) {
            try {
                String absolutePath = file.getAbsolutePath();
                String canonicalPath = file.getCanonicalPath();
                if (StringUtils.equalsIgnoreCase(absolutePath, canonicalPath)) {
                    return canonicalPath;
                }
            } catch (Throwable e) {
            }
        }
        return path;
    }

    /**
     * Return a directory with the given name in the given base directory. If the directory did not
     * already exist it will be created. If there is a file of the same name in the base directory,
     * then the directory name will be made unique by appending an integer to the base name.
     * 
     * @return a directory with the given name in the given base directory
     * @throws SecurityException if the directory cannot be accessed or created
     */
    public static File getOrCreateDirectory(File baseDirectory, String baseName) {
        File directory;
        int index;

        directory = new File(baseDirectory, baseName);
        index = 1;
        while (directory.exists() && !directory.isDirectory()) {
            directory = new File(baseDirectory, baseName + index);
            index++;
        }
        if (!directory.exists()) {
            directory.mkdir();
        }
        return directory;
    }

    /**
     * Return a file in the given directory whose name is composed from the given base name and
     * extension (which should include the period), but which does not currently exist.
     * 
     * @param directory the directory that should contain the file
     * @param baseName the base name of the file
     * @param extension the extension used for the file
     * @return a unique file that can be created without overwriting any other file
     */
    public static File getUniqueFile(File directory, String baseName, String extension) {
        File file;
        int index;

        file = new File(directory, baseName + extension);
        index = 1;
        while (file.exists()) {
            file = new File(directory, baseName + index + extension);
            index++;
        }
        return file;
    }

    /**
     * Returns whether the given file is a symlinked file.
     * 
     * @param file
     * @return
     * @throws CoreException
     * @throws IOException
     */
    public static boolean isLinkedFile(File file) throws CoreException {
        IFileStore fileStore = EFS.getStore(file.toURI());

        IFileInfo info = fileStore.fetchInfo();

        return info.getAttribute(EFS.ATTRIBUTE_SYMLINK);
    }

    /**
     * Return <code>true</code> if the given parent directory is either the same as or a parent of the
     * given child directory.
     * 
     * @param parentDirectory the directory that might be a parent of the child directory
     * @param childDirectory the directory that might be a child of the parent directory
     * @return <code>true</code> if the given parent directory is a parent of the given child
     *         directory
     */
    public static boolean isParentOf(File parentDirectory, File childDirectory) {
        File directory = childDirectory;
        while (directory != null) {
            if (parentDirectory.equals(directory)) {
                return true;
            }
            directory = directory.getParentFile();
        }
        return false;
    }

    /**
     * Attempt to make the given file executable.
     * 
     * @param file the file to be made executable
     * @return {@code true} if the file is executable
     */
    public static boolean makeExecutable(File file) {
        // Try to make the file executable for all users.
        if (file.setExecutable(true, false)) {
            return true;
        }
        // If that fails, then try to make it executable for the current user.
        return file.setExecutable(true, true);
    }

    /**
     * Overwrite the contents of the given file to the given contents.
     * 
     * @param file the file whose contents are to be written
     * @param contents the new contents for the file
     * @throws IOException if the file contents could not be written
     */
    public static void setContents(File file, String contents) throws IOException {
        FileWriter fileWriter = null;
        BufferedWriter writer;

        try {
            fileWriter = new FileWriter(file);
            writer = new BufferedWriter(fileWriter);
            writer.write(contents);
            writer.flush();
        } finally {
            if (fileWriter != null) {
                fileWriter.close();
            }
        }
    }

    /**
     * Return <code>true</code> if the given character can be included in a valid file name.
     * 
     * @param character the character being tested
     * @return <code>true</code> if the given character can be included in a valid file name
     */
    private static boolean isValidFileNameChar(char character) {
        if (Character.isWhitespace(character)) {
            return false;
        }
        String invalidChars;
        //    if (OS.isWindows()) {
        invalidChars = "\\/:*?\"<>|";
        //    } else if (OS.isMacOSX()) {
        //        invalidChars = "/:";
        //    } else { // assume Unix/Linux
        //        invalidChars = "/";
        //    }

        return !((invalidChars.indexOf(character) >= 0) // OS-invalid
                || (character < '\u0020') // ctrls
                || (character > '\u007e' && character < '\u00a0')); // ctrls
    }

    /**
     * Delete the contents of the given directory, given that we know it is a directory.
     * 
     * @param directory the directory whose contents are to be deleted
     */
    private static void safelyDeleteContents(File directory) {
        File[] children;

        children = directory.listFiles();
        for (int i = 0; i < children.length; i++) {
            delete(children[i]);
        }
    }

    /**
     * Disallow the creation of instances of this class.
     */
    private FileUtilities() {
    }
}