de.fu_berlin.inf.dpp.core.util.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for de.fu_berlin.inf.dpp.core.util.FileUtils.java

Source

/*
 *
 *  DPP - Serious Distributed Pair Programming
 *  (c) Freie Universitt Berlin - Fachbereich Mathematik und Informatik - 2010
 *  (c) NFQ (www.nfq.com) - 2014
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * /
 */

package de.fu_berlin.inf.dpp.core.util;

import de.fu_berlin.inf.dpp.core.monitor.IProgressMonitor;
import de.fu_berlin.inf.dpp.core.workspace.IWorkspace;
import de.fu_berlin.inf.dpp.core.workspace.IWorkspaceRunnable;
import de.fu_berlin.inf.dpp.filesystem.*;
import de.fu_berlin.inf.dpp.util.Pair;
import de.fu_berlin.inf.dpp.util.StackTrace;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.picocontainer.annotations.Inject;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.zip.Adler32;

public class FileUtils {

    private static final int BUFFER_SIZE = 32 * 1024;
    @Inject
    public static IWorkspace workspace;
    private static Logger log = Logger.getLogger(FileUtils.class);

    private FileUtils() {
        // no instantiation allowed
    }

    /**
     * Calculate Adler32 checksum for given file.
     *
     * @return checksum of file
     * @throws IOException if checksum calculation has been failed.
     */
    public static long checksum(IFile file) throws IOException {

        InputStream in;
        try {
            in = file.getContents();
        } catch (IOException e) {
            throw new IOException("failed to calculate checksum.", e);
        }

        byte[] buffer = new byte[BUFFER_SIZE];

        Adler32 adler = new Adler32();

        int read;

        try {
            while ((read = in.read(buffer)) != -1) {
                adler.update(buffer, 0, read);
            }
        } finally {
            IOUtils.closeQuietly(in);
        }

        return adler.getValue();
    }

    /**
     * Makes the given file read-only (</code>readOnly == true</code>) or
     * writable (<code>readOnly == false</code>).
     *
     * @param file     the resource whose read-only attribute is to be set or removed
     * @param readOnly <code>true</code> to set the given file to be read-only,
     *                 <code>false</code> to make writable
     * @return The state before setting read-only to the given value.
     */
    public static boolean setReadOnly(IResource file, boolean readOnly) {

        IResourceAttributes attributes = file.getResourceAttributes();

        if (attributes == null) {
            // TODO Throw a FileNotFoundException and deal with it everywhere!
            log.error("File does not exist for setting readOnly == " + readOnly + ": " + file, new StackTrace());
            return false;
        }
        boolean result = attributes.isReadOnly();

        // Already in desired state
        if (result == readOnly) {
            return result;
        }

        attributes.setReadOnly(readOnly);
        try {
            file.setResourceAttributes(attributes);
        } catch (IOException e) {
            // failure is not an option
            log.warn("Failed to set resource readonly == " + readOnly + ": " + file);
        }
        return result;
    }

    /**
     * Writes the given input stream to the given file.
     * <p/>
     * This operation will removeAll a possible readOnly flag and re-set if after
     * the operation.
     *
     * @param input the input stream to write to the file
     * @param file  the file to create/overwrite
     * @throws IOException if the file could not be written.
     * @blocking This operations blocks until the operation is reported as
     * finished by Eclipse.
     */
    public static void writeFile(InputStream input, IFile file, IProgressMonitor monitor) throws IOException {
        if (file.exists()) {
            updateFile(input, file, monitor);
        } else {
            createFile(input, file, monitor);
        }

    }

    /**
     * Move the file to the same location, adding the file extension "BACKUP" or
     * "_BACKUP_X" on file name where X is a number that matches a not used file
     * name.
     *
     * @param file    the {@link IFile} to rename
     * @param monitor a progress monitor to show progress to user
     * @throws IOException
     * @throws FileNotFoundException
     */
    public static void backupFile(IFile file, IProgressMonitor monitor) throws IOException, FileNotFoundException {

        if (!file.exists()) {
            throw new FileNotFoundException();
        }

        IProject project = file.getProject();

        IPath originalBackupPath = file.getProjectRelativePath().addFileExtension("BACKUP");

        IPath backupPath = originalBackupPath;

        for (int i = 0; i < 1000; i++) {
            if (!project.exists(backupPath)) {
                break;
            }

            backupPath = originalBackupPath.removeFileExtension().addFileExtension("BACKUP_" + i);
        }

        file.move(file.getFullPath().removeLastSegments(1).append(backupPath.lastSegment()), true);

    }

    /**
     * Creates the given file and any missing parent directories.
     * <p/>
     * This method will try to removeAll read-only settings on the parent
     * directories and reset them at the end of the operation.
     *
     * @pre the file must not exist. Use writeFile() for getting this cases
     * handled.
     */
    public static void createFile(final InputStream input, final IFile file, IProgressMonitor monitor)
            throws IOException {

        IWorkspaceRunnable createFileProcedure = new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws IOException {
                // Make sure directory exists
                mkdirs(file);

                // Make sure that parent is writable
                IContainer parent = file.getParent();
                boolean wasReadOnly = false;
                if (parent != null) {
                    wasReadOnly = setReadOnly(parent, false);
                }

                file.create(input, true);

                // Reset permissions on parent
                if (parent != null && wasReadOnly) {
                    setReadOnly(parent, true);
                }

            }
        };

        workspace.run(createFileProcedure, workspace.getRoot(), IWorkspace.AVOID_UPDATE, monitor);

    }

    /**
     * Updates the data in the file with the data from the given InputStream.
     *
     * @pre the file must exist
     */
    public static void updateFile(final InputStream input, final IFile file, IProgressMonitor monitor)
            throws IOException {

        IWorkspaceRunnable replaceFileProcedure = new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws IOException {

                file.setContents(input, true, true);

            }
        };

        workspace.run(replaceFileProcedure, workspace.getRoot(), IWorkspace.AVOID_UPDATE, monitor);
    }

    /**
     * Makes sure that the parent directories of the given IResource exist,
     * possibly removing write protection.
     */

    public static IFolder getParentFolder(IResource resource) {

        if (resource == null) {
            return null;
        }
        IContainer parent = resource.getParent();
        if (parent == null || parent.getType() != IResource.FOLDER) {
            return null;
        }
        return (IFolder) parent;
    }

    public static void create(final IFolder folder) throws IOException {

        // if ((folder == null) || (folder.exists())) {
        // log.debug(".create() Creating folder not possible");
        // return;
        // }

        if (folder == null) {
            log.warn(".create() Creating folder not possible -  it is null");
            throw new IllegalArgumentException();
        }
        if (folder.exists()) {
            log.debug(".create() Creating folder " + folder.getName() + " not possible - it already exists");
            return;
        }
        IWorkspaceRunnable createFolderProcedure = new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws IOException {

                // recursively create folders until parent folder exists
                // or project root is reached
                IFolder parentFolder = getParentFolder(folder);
                if (parentFolder != null) {
                    create(parentFolder);
                }

                folder.create(IResource.NONE, true);

                if (monitor.isCanceled()) {
                    log.warn("Creating folder failed: " + folder);
                }

            }
        };

        workspace.run(createFolderProcedure, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);

    }

    public static void delete(final IResource resource) throws IOException {
        if (!resource.exists()) {
            log.warn("File not found for deletion: " + resource, new StackTrace());
            return;
        }

        IWorkspaceRunnable deleteProcedure = new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws IOException {
                if (!resource.exists()) {
                    return;
                }

                if (resource.getResourceAttributes() == null) {
                    return;
                }

                setReadOnly(resource, false);

                resource.delete(IResource.FORCE | IResource.KEEP_HISTORY);

                if (monitor.isCanceled()) {
                    log.warn("Removing resource failed: " + resource);
                }
            }
        };

        workspace.run(deleteProcedure, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);

    }

    /**
     * Moves the given {@link IResource} to the place, that is pointed by the
     * given {@link IPath}.
     * <p/>
     * This method excepts both variables to be relative to the workspace.
     *
     * @param destination Destination of moving the given resource.
     * @param source      Resource, that is going to be moved
     */
    public static void move(final IPath destination, final IResource source) throws IOException {

        log.trace(".move(" + destination.toOSString() + " , " + source.getName() + ")");

        if (!source.isAccessible()) {
            log.warn(".move Source file can not be accessed  " + source.getFullPath());
            return;
        }

        IWorkspaceRunnable moveProcedure = new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws IOException {
                IPath absDestination = destination.makeAbsolute();

                source.move(absDestination, false);

                if (monitor.isCanceled()) {
                    log.warn("Moving resource failed (Cancel Button pressed).");
                }
            }
        };

        workspace.run(moveProcedure, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);

    }

    /**
     * Makes sure that the parent directories of the given IResource exist,
     * possibly removing write protection.
     */
    public static boolean mkdirs(IResource resource) {

        if (resource == null) {
            return true;
        }

        IFolder parent = getParentFolder(resource);
        if (parent == null || parent.exists()) {
            return true;
        }

        IContainer root = parent;
        while (!root.exists()) {
            IContainer temp = root.getParent();
            if (temp == null) {
                break;
            }
            root = temp;
        }
        boolean wasReadOnly = FileUtils.setReadOnly(root, false);

        try {
            create(parent);
        } catch (IOException e) {
            log.error("Could not create Dir: " + parent.getFullPath());
            return false;
        } finally {
            if (wasReadOnly) {
                FileUtils.setReadOnly(root, true);
            }
        }
        return true;
    }

    /**
     * Calculates the total file count and size for all resources.
     *
     * @param resources      collection containing the resources that file sizes and file
     *                       count should be calculated
     * @param includeMembers <code>true</code> to include the members of resources that
     *                       represents a {@linkplain IContainer container}
     * @param flags          additional flags on how to process the members of containers
     * @return a pair containing the {@linkplain de.fu_berlin.inf.dpp.util.Pair#p file size} and
     * {@linkplain de.fu_berlin.inf.dpp.util.Pair#v file count} for the given resources
     */
    public static Pair<Long, Long> getFileCountAndSize(Collection<? extends IResource> resources,
            boolean includeMembers, int flags) {
        long totalFileSize = 0;
        long totalFileCount = 0;

        Pair<Long, Long> fileCountAndSize = new Pair<Long, Long>(0L, 0L);

        for (IResource resource : resources) {
            switch (resource.getType()) {
            case IResource.FILE:
                totalFileCount++;

                try {
                    long filesize = -1; //todo // EFS.getStore(resource.getLocationURI()).fetchInfo().getLength();

                    totalFileSize += filesize;
                } catch (Exception e) {
                    log.warn("failed to retrieve file size of file " + resource.getLocationURI(), e);
                }
                break;
            case IResource.PROJECT:
            case IResource.FOLDER:
                if (!includeMembers) {
                    break;
                }

                try {
                    IContainer container = ((IContainer) resource.getAdapter(IContainer.class));

                    Pair<Long, Long> subFileCountAndSize = FileUtils
                            .getFileCountAndSize(Arrays.asList(container.members(flags)), includeMembers, flags);

                    totalFileSize += subFileCountAndSize.p;
                    totalFileCount += subFileCountAndSize.v;

                } catch (Exception e) {
                    log.warn("failed to process container: " + resource, e);
                }
                break;
            default:
                break;
            }
        }
        fileCountAndSize.p = totalFileSize;
        fileCountAndSize.v = totalFileCount;
        return fileCountAndSize;
    }

    /**
     * Retrieves the content of a local file
     *
     * @param localFile
     * @return Byte array of the file contents. Is <code>null</code> if the file
     * does not exist or is out of sync, the reference points to no
     * file, or the conversion to a byte array failed.
     */
    public static byte[] getLocalFileContent(IFile localFile) {

        InputStream in = null;
        byte[] content = null;
        try {
            in = localFile.getContents();
        } catch (IOException e) {
            log.warn("could not get content of file " + localFile.getFullPath());
        }

        if (in == null) {
            return null;
        }

        try {
            content = IOUtils.toByteArray(in);
        } catch (IOException e) {
            log.warn("could not convert file content to byte array (file: " + localFile.getFullPath() + ")");
        } finally {
            IOUtils.closeQuietly(in);
        }
        return content;
    }

}