com.liferay.ide.theme.core.util.BuildHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.ide.theme.core.util.BuildHelper.java

Source

/*******************************************************************************
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library 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 Lesser General Public License for more
 * details.
 *
 * Contributors:
 *       Gregory Amerson - initial implementation and ongoing maintenance
 *******************************************************************************/
package com.liferay.ide.theme.core.util;

import com.liferay.ide.theme.core.ThemeCore;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.server.core.internal.Messages;
import org.eclipse.wst.server.core.internal.ProgressUtil;

/**
 * @author Gregory Amerson
 */
@SuppressWarnings("restriction")
public class BuildHelper {

    // size of the buffer
    private static final int BUFFER = 65536;

    // the buffer
    private static byte[] buf = new byte[BUFFER];

    private static final IStatus[] EMPTY_STATUS = new IStatus[0];

    private static final File defaultTempDir = ThemeCore.getDefault().getStateLocation().toFile();

    private static final String TEMPFILE_PREFIX = ".tmp-safe-to-delete-"; //$NON-NLS-1$

    private File tempDir;

    /**
     * Create a new PublishHelper.
     *
     * @param tempDirectory
     *            a temporary directory to use during publishing, or <code>null</code> to use the default. If it does
     *            not exist, the folder will be created
     */
    public BuildHelper() {
        tempDir = defaultTempDir;
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }
    }

    /**
     * Copy a file from a to b. Closes the input stream after use.
     *
     * @param in
     *            an input stream
     * @param to
     *            a path to copy to. the directory must already exist
     * @param ts
     *            timestamp
     * @throws CoreException
     *             if anything goes wrong
     */
    private void copyFile(InputStream in, IPath to, long ts, IFile mf) throws CoreException {
        OutputStream out = null;

        File tempFile = null;
        File tempFileParentDir = null;

        try {
            File file = to.toFile();

            // IDE-796 need to make sure temporary file is generated in same directory as file destination so that
            // file.renameTo() will never fail due to source/destination being on two different file systems
            if (file != null && file.getParentFile().exists()) {
                tempFileParentDir = to.toFile().getParentFile();
            } else {
                tempFileParentDir = tempDir;
            }

            tempFile = File.createTempFile(TEMPFILE_PREFIX, "." + to.getFileExtension(), tempFileParentDir); //$NON-NLS-1$

            out = new FileOutputStream(tempFile);

            int avail = in.read(buf);
            while (avail > 0) {
                out.write(buf, 0, avail);
                avail = in.read(buf);
            }

            out.close();
            out = null;

            moveTempFile(tempFile, file);

            if (ts != IResource.NULL_STAMP && ts != 0)
                file.setLastModified(ts);
        } catch (CoreException e) {
            throw e;
        } catch (Exception e) {
        } finally {
            if (tempFile != null && tempFile.exists())
                tempFile.deleteOnExit();
            try {
                if (in != null)
                    in.close();
            } catch (Exception ex) {
                // ignore
            }
            try {
                if (out != null)
                    out.close();
            } catch (Exception ex) {
                // ignore
            }
        }
    }

    /**
     * Utility method to recursively delete a directory.
     *
     * @param dir
     *            a directory
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public static IStatus[] deleteDirectory(File dir, IProgressMonitor monitor) {
        if (!dir.exists() || !dir.isDirectory())
            return new IStatus[] { new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                    NLS.bind(Messages.errorNotADirectory, dir.getAbsolutePath()), null) };

        List<IStatus> status = new ArrayList<IStatus>(2);

        try {
            File[] files = dir.listFiles();
            int size = files.length;
            monitor = ProgressUtil.getMonitorFor(monitor);
            monitor.beginTask(NLS.bind(Messages.deletingTask, new String[] { dir.getAbsolutePath() }), size * 10);

            // cycle through files
            boolean deleteCurrent = true;
            for (int i = 0; i < size; i++) {
                File current = files[i];
                if (current.isFile()) {
                    if (!current.delete()) {
                        status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                                NLS.bind(Messages.errorDeleting, files[i].getAbsolutePath()), null));
                        deleteCurrent = false;
                    }
                    monitor.worked(10);
                } else if (current.isDirectory()) {
                    monitor.subTask(NLS.bind(Messages.deletingTask, new String[] { current.getAbsolutePath() }));
                    IStatus[] stat = deleteDirectory(current, ProgressUtil.getSubMonitorFor(monitor, 10));
                    if (stat != null && stat.length > 0) {
                        deleteCurrent = false;
                        addArrayToList(status, stat);
                    }
                }
            }
            if (deleteCurrent && !dir.delete())
                status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                        NLS.bind(Messages.errorDeleting, dir.getAbsolutePath()), null));
            monitor.done();
        } catch (Exception e) {
            ThemeCore.logError("Error deleting directory " + dir.getAbsolutePath(), e); //$NON-NLS-1$
            status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0, e.getLocalizedMessage(), null));
        }

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    /**
     * Smart copy the given module resources to the given path.
     *
     * @param resources
     *            an array of module resources
     * @param path
     *            an external path to copy to
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishSmart(IResource[] resources, IPath path, IProgressMonitor monitor) {
        return publishSmart(resources, path, null, monitor);
    }

    /**
     * Smart copy the given module resources to the given path.
     *
     * @param resources
     *            an array of module resources
     * @param path
     *            an external path to copy to
     * @param ignore
     *            an array of paths relative to path to ignore, i.e. not delete or copy over
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishSmart(IResource[] resources, IPath path, IPath[] ignore, IProgressMonitor monitor) {
        if (resources == null)
            return EMPTY_STATUS;

        monitor = ProgressUtil.getMonitorFor(monitor);

        List<IStatus> status = new ArrayList<IStatus>(2);
        File toDir = path.toFile();
        int fromSize = resources.length;
        String[] fromFileNames = new String[fromSize];
        for (int i = 0; i < fromSize; i++)
            fromFileNames[i] = resources[i].getName();
        List<String> ignoreFileNames = new ArrayList<String>();
        if (ignore != null) {
            for (int i = 0; i < ignore.length; i++) {
                if (ignore[i].segmentCount() == 1) {
                    ignoreFileNames.add(ignore[i].toOSString());
                }
            }
        }

        // cache files and file names for performance
        File[] toFiles = null;
        String[] toFileNames = null;

        boolean foundExistingDir = false;
        if (toDir.exists()) {
            if (toDir.isDirectory()) {
                foundExistingDir = true;
                toFiles = toDir.listFiles();
                int toSize = toFiles.length;
                toFileNames = new String[toSize];

                // check if this exact file exists in the new directory
                for (int i = 0; i < toSize; i++) {
                    toFileNames[i] = toFiles[i].getName();
                    boolean isDir = toFiles[i].isDirectory();
                    boolean found = false;
                    for (int j = 0; j < fromSize; j++) {
                        if (toFileNames[i].equals(fromFileNames[j]) && isDir == resources[j] instanceof IFolder) {
                            found = true;
                            break;
                        }
                    }

                    // delete file if it can't be found or isn't the correct type
                    if (!found) {
                        boolean delete = true;
                        // if should be preserved, don't delete and don't try to copy
                        for (String preserveFileName : ignoreFileNames) {
                            if (toFileNames[i].equals(preserveFileName)) {
                                delete = false;
                                break;
                            }
                        }
                        if (delete) {
                            if (isDir) {
                                IStatus[] stat = deleteDirectory(toFiles[i], null);
                                addArrayToList(status, stat);
                            } else {
                                if (!toFiles[i].delete())
                                    status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                                            NLS.bind(Messages.errorDeleting, toFiles[i].getAbsolutePath()), null));
                            }
                        }
                        toFiles[i] = null;
                        toFileNames[i] = null;
                    }
                }
            } else { // if (toDir.isFile())
                if (!toDir.delete()) {
                    status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                            NLS.bind(Messages.errorDeleting, toDir.getAbsolutePath()), null));
                    IStatus[] stat = new IStatus[status.size()];
                    status.toArray(stat);
                    return stat;
                }
            }
        }
        if (!foundExistingDir && !toDir.mkdirs()) {
            status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                    NLS.bind(Messages.errorMkdir, toDir.getAbsolutePath()), null));
            IStatus[] stat = new IStatus[status.size()];
            status.toArray(stat);
            return stat;
        }

        if (monitor.isCanceled())
            return new IStatus[] { Status.CANCEL_STATUS };

        monitor.worked(50);

        // cycle through files and only copy when it doesn't exist
        // or is newer
        if (toFiles == null) {
            toFiles = toDir.listFiles();
            if (toFiles == null)
                toFiles = new File[0];
        }
        int toSize = toFiles.length;

        int dw = 0;
        if (toSize > 0)
            dw = 500 / toSize;

        // cache file names and last modified dates for performance
        if (toFileNames == null)
            toFileNames = new String[toSize];
        long[] toFileMod = new long[toSize];
        for (int i = 0; i < toSize; i++) {
            if (toFiles[i] != null) {
                if (toFileNames[i] != null)
                    toFileNames[i] = toFiles[i].getName();
                toFileMod[i] = toFiles[i].lastModified();
            }
        }

        for (int i = 0; i < fromSize; i++) {
            IResource current = resources[i];
            String name = fromFileNames[i];
            boolean currentIsDir = current instanceof IFolder;

            if (!currentIsDir) {
                // check if this is a new or newer file
                boolean copy = true;
                IFile mf = (IFile) current;

                long mod = -1;
                IFile file = (IFile) mf.getAdapter(IFile.class);
                if (file != null) {
                    mod = file.getLocalTimeStamp();
                } else {
                    File file2 = (File) mf.getAdapter(File.class);
                    mod = file2.lastModified();
                }

                for (int j = 0; j < toSize; j++) {
                    if (name.equals(toFileNames[j]) && mod == toFileMod[j]) {
                        copy = false;
                        break;
                    }
                }

                if (copy) {
                    try {
                        copyFile(mf, path.append(name));
                    } catch (CoreException ce) {
                        status.add(ce.getStatus());
                    }
                }
                monitor.worked(dw);
            } else { // if (currentIsDir) {
                IFolder folder = (IFolder) current;
                IResource[] children = null;

                try {
                    children = folder.members();
                } catch (CoreException e) {
                    e.printStackTrace();
                }

                // build array of ignored Paths that apply to this folder
                IPath[] ignoreChildren = null;
                if (ignore != null) {
                    List<IPath> ignoreChildPaths = new ArrayList<IPath>();
                    for (int j = 0; j < ignore.length; j++) {
                        IPath preservePath = ignore[j];
                        if (preservePath.segment(0).equals(name)) {
                            ignoreChildPaths.add(preservePath.removeFirstSegments(1));
                        }
                    }
                    if (ignoreChildPaths.size() > 0)
                        ignoreChildren = ignoreChildPaths.toArray(new Path[ignoreChildPaths.size()]);
                }
                monitor.subTask(NLS.bind(Messages.copyingTask, new String[] { name, name }));
                IStatus[] stat = publishSmart(children, path.append(name), ignoreChildren,
                        ProgressUtil.getSubMonitorFor(monitor, dw));
                addArrayToList(status, stat);
            }
        }
        if (monitor.isCanceled())
            return new IStatus[] { Status.CANCEL_STATUS };

        monitor.worked(500 - dw * toSize);
        monitor.done();

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    /**
     * Handle a delta publish.
     *
     * @param delta
     *            a module resource delta
     * @param path
     *            the path to publish to
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishDelta(IResourceDelta[] delta, IPath path, IPath[] restorePaths,
            IProgressMonitor monitor) {
        if (delta == null)
            return EMPTY_STATUS;

        monitor = ProgressUtil.getMonitorFor(monitor);

        List<IStatus> status = new ArrayList<IStatus>(2);
        int size2 = delta.length;
        for (int i = 0; i < size2; i++) {
            IStatus[] stat = publishDelta(delta[i], path, restorePaths, monitor);
            addArrayToList(status, stat);
        }

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    /**
     * Handle a delta publish.
     *
     * @param delta
     *            a module resource delta
     * @param path
     *            the path to publish to
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishDelta(IResourceDelta delta, IPath path, IPath[] restorePaths,
            IProgressMonitor monitor) {
        List<IStatus> status = new ArrayList<IStatus>(2);

        IResource resource = delta.getResource();
        int kind2 = delta.getKind();

        if (resource instanceof IFile) {
            IFile file = (IFile) resource;

            try {
                if (kind2 == IResourceDelta.REMOVED) {
                    deleteFile(path, file, restorePaths);
                } else {
                    IPath diffsRelativePath = getDiffsRelativePath(file.getProjectRelativePath());

                    if (diffsRelativePath != null) {
                        IPath path2 = path.append(diffsRelativePath);
                        File f = path2.toFile().getParentFile();

                        if (!f.exists()) {
                            f.mkdirs();
                        }

                        copyFile(file, path2);
                    }
                }
            } catch (CoreException ce) {
                status.add(ce.getStatus());
            }

            IStatus[] stat = new IStatus[status.size()];
            status.toArray(stat);
            return stat;
        }

        if (kind2 == IResourceDelta.ADDED) {
            // find relative path from _diffs and append that to path.
            IPath diffsPath = resource.getProjectRelativePath();
            IPath diffsRelativePath = getDiffsRelativePath(diffsPath);

            if (diffsRelativePath != null) {
                IPath path2 = path.append(diffsRelativePath);
                //                IPath path2 = path.append( resource.getProjectRelativePath() ).append( resource.getName() );
                File file = path2.toFile();
                if (!file.exists() && !file.mkdirs()) {
                    status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                            NLS.bind(Messages.errorMkdir, path2), null));
                    IStatus[] stat = new IStatus[status.size()];
                    status.toArray(stat);
                    return stat;
                }
            }
        }

        IResourceDelta[] childDeltas = delta.getAffectedChildren();
        int size = childDeltas.length;
        for (int i = 0; i < size; i++) {
            IStatus[] stat = publishDelta(childDeltas[i], path, restorePaths, monitor);
            addArrayToList(status, stat);
        }

        if (kind2 == IResourceDelta.REMOVED) {
            IPath diffsRelativePath = getDiffsRelativePath(resource.getProjectRelativePath());

            if (diffsRelativePath != null) {
                IPath path2 = path.append(diffsRelativePath);
                //IPath path2 = path.append( resource.getProjectRelativePath() ).append( resource.getName() );
                File file = path2.toFile();

                if (file.exists() && !file.delete()) {
                    status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                            NLS.bind(Messages.errorDeleting, path2), null));
                }
            }
        }

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    private static IPath getDiffsRelativePath(IPath diffsPath) {
        IPath diffsRelativePath = null;

        for (int i = 0; i < diffsPath.segmentCount(); i++) {
            if ("_diffs".equals(diffsPath.segment(i))) //$NON-NLS-1$
            {
                diffsRelativePath = diffsPath.removeFirstSegments(i + 1);
                break;
            }
        }

        return diffsRelativePath;
    }

    private static void deleteFile(IPath path, IFile file, IPath[] restorePaths) throws CoreException {
        IPath diffsPath = file.getProjectRelativePath();
        IPath diffsRelativePath = getDiffsRelativePath(diffsPath);

        if (diffsRelativePath != null) {
            //            IPath path2 = path.append( file.getProjectRelativePath() ).append( file.getName() );
            IPath path2 = path.append(diffsRelativePath);

            // restore this file from the first restorePaths that matches
            boolean restored = false;

            for (IPath restorePath : restorePaths) {
                final File restoreFile = restorePath.append(diffsRelativePath).toFile();

                if (restoreFile.exists()) {
                    try {
                        FileUtils.copyFile(restoreFile, path2.toFile());
                        restored = true;
                        break;
                    } catch (IOException e) {
                        throw new CoreException(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                                NLS.bind("Error restoring theme file.", path2), null)); //$NON-NLS-1$
                    }
                }
            }

            if (!restored) {
                if (path2.toFile().exists() && !path2.toFile().delete()) {
                    throw new CoreException(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                            NLS.bind(Messages.errorDeleting, path2), null));
                }
            }
        }
    }

    private void copyFile(IFile mf, IPath path) throws CoreException {
        if (!isCopyFile(mf, path)) {
            return;
        }

        IFile file = (IFile) mf.getAdapter(IFile.class);
        if (file != null)
            copyFile(file.getContents(), path, file.getLocalTimeStamp(), mf);
        else {
            File file2 = (File) mf.getAdapter(File.class);
            InputStream in = null;
            try {
                in = new FileInputStream(file2);
            } catch (IOException e) {
                throw new CoreException(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                        NLS.bind(Messages.errorReading, file2.getAbsolutePath()), e));
            }
            copyFile(in, path, file2.lastModified(), mf);
        }
    }

    /**
     * Returns <code>true<code/> if the module file should be copied to the destination, <code>false</codre> otherwise.
     *
     * @param moduleFile
     *            the module file
     * @param toPath
     *            destination.
     * @return <code>true<code/>, if the module file should be copied
     */
    protected boolean isCopyFile(IFile moduleFile, IPath toPath) {
        return true;
    }

    /**
     * Publish the given module resources to the given path.
     *
     * @param resources
     *            an array of module resources
     * @param path
     *            a path to publish to
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishFull(IResource[] resources, IPath path, IProgressMonitor monitor) {
        if (resources == null)
            return EMPTY_STATUS;

        monitor = ProgressUtil.getMonitorFor(monitor);

        List<IStatus> status = new ArrayList<IStatus>(2);
        int size = resources.length;
        for (int i = 0; i < size; i++) {
            IStatus[] stat = copy(resources[i], path, monitor);
            addArrayToList(status, stat);

            if (monitor.isCanceled()) {
                break;
            }
        }

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    private IStatus[] copy(IResource resource, IPath path, IProgressMonitor monitor) {
        if (monitor != null && monitor.isCanceled()) {
            return new IStatus[0];
        }

        List<IStatus> status = new ArrayList<IStatus>(2);

        if (resource instanceof IFolder) {
            IFolder folder = (IFolder) resource;
            IStatus[] stat;
            try {
                stat = publishFull(folder.members(), path, monitor);
                addArrayToList(status, stat);
            } catch (CoreException e) {
                e.printStackTrace();
            }

        } else {
            IFile mf = (IFile) resource;

            IPath diffsRelativePath = getDiffsRelativePath(mf.getProjectRelativePath());

            if (diffsRelativePath != null) {
                //              path = path.append( mf.getProjectRelativePath() ).append( name );
                path = path.append(diffsRelativePath);

                File f = path.toFile().getParentFile();

                if (f.exists()) {
                    try {
                        copyFile(mf, path);
                    } catch (CoreException ce) {
                        status.add(ce.getStatus());
                    }
                } else {
                    // Create the parent directory.
                    if (f.mkdirs()) {
                        try {
                            copyFile(mf, path);
                        } catch (CoreException ce) {
                            status.add(ce.getStatus());
                        }
                    } else {
                        status.add(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                                NLS.bind(Messages.errorMkdir, f.getAbsolutePath()), null));
                    }
                }
            }
        }

        IStatus[] stat = new IStatus[status.size()];
        status.toArray(stat);
        return stat;
    }

    /**
     * Accepts an IModuleResource array which is expected to contain a single IModuleFile resource and copies it to the
     * specified path, which should include the name of the file to write. If the array contains more than a single
     * resource or the resource is not an IModuleFile resource, the file is not created. Currently no error is returned,
     * but error handling is recommended since that is expected to change in the future.
     *
     * @param resources
     *            an array containing a single IModuleFile resource
     * @param path
     *            the path, including file name, where the file should be created
     * @param monitor
     *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
     * @return a possibly-empty array of error and warning status
     */
    public IStatus[] publishToPath(IResource[] resources, IPath path, IProgressMonitor monitor) {
        if (resources == null || resources.length == 0) {
            // should also check if resources consists of all empty directories
            File file = path.toFile();
            if (file.exists())
                file.delete();
            return EMPTY_STATUS;
        }

        monitor = ProgressUtil.getMonitorFor(monitor);

        if (resources.length == 1 && resources[0] instanceof IFile) {
            try {
                copyFile((IFile) resources[0], path);
            } catch (CoreException e) {
                return new IStatus[] { e.getStatus() };
            }
        }

        return EMPTY_STATUS;
    }

    /**
     * Utility method to move a temp file into position by deleting the original and swapping in a new copy.
     *
     * @param tempFile
     * @param file
     * @throws CoreException
     */
    private void moveTempFile(File tempFile, File file) throws CoreException {
        if (file.exists()) {
            if (!safeDelete(file, 2)) {
                // attempt to rewrite an existing file with the tempFile contents if
                // the existing file can't be deleted to permit the move
                try {
                    InputStream in = new FileInputStream(tempFile);
                    IStatus status = copyFile(in, file.getPath());
                    if (!status.isOK()) {
                        MultiStatus status2 = new MultiStatus(ThemeCore.PLUGIN_ID, 0,
                                NLS.bind(Messages.errorDeleting, file.toString()), null);
                        status2.add(status);
                        throw new CoreException(status2);
                    }
                    return;
                } catch (FileNotFoundException e) {
                    // shouldn't occur
                } finally {
                    tempFile.delete();
                }
                /*
                 * if (!safeDelete(file, 8)) { tempFile.delete(); throw new CoreException(new Status(IStatus.ERROR,
                 * ThemeCore.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, file.toString()), null)); }
                 */
            }
        }
        if (!safeRename(tempFile, file, 10))
            throw new CoreException(new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                    NLS.bind(Messages.errorRename, tempFile.toString()), null));
    }

    /**
     * Copy a file from a to b. Closes the input stream after use.
     *
     * @param in
     *            an InputStream
     * @param to
     *            the file to copy to
     * @return a status
     */
    private IStatus copyFile(InputStream in, String to) {
        OutputStream out = null;

        try {
            out = new FileOutputStream(to);

            int avail = in.read(buf);
            while (avail > 0) {
                out.write(buf, 0, avail);
                avail = in.read(buf);
            }
            return Status.OK_STATUS;
        } catch (Exception e) {
            ThemeCore.logError("Error copying file", e); //$NON-NLS-1$
            return new Status(IStatus.ERROR, ThemeCore.PLUGIN_ID, 0,
                    NLS.bind(Messages.errorCopyingFile, new String[] { to, e.getLocalizedMessage() }), e);
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Exception ex) {
                // ignore
            }
            try {
                if (out != null)
                    out.close();
            } catch (Exception ex) {
                // ignore
            }
        }
    }

    /**
     * Safe delete. Tries to delete multiple times before giving up.
     *
     * @param f
     * @return <code>true</code> if it succeeds, <code>false</code> otherwise
     */
    private static boolean safeDelete(File f, int retrys) {
        int count = 0;
        while (count < retrys) {
            if (f.delete()) {
                return true;
            }

            count++;
            // delay if we are going to try again
            if (count < retrys) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    // ignore
                }
            }
        }
        return false;
    }

    /**
     * Safe rename. Will try multiple times before giving up.
     *
     * @param from
     * @param to
     * @param retrys
     *            number of times to retry
     * @return <code>true</code> if it succeeds, <code>false</code> otherwise
     */
    private static boolean safeRename(File from, File to, int retrys) {
        // make sure parent dir exists
        File dir = to.getParentFile();
        if (dir != null && !dir.exists())
            dir.mkdirs();

        int count = 0;
        while (count < retrys) {
            if (from.renameTo(to))
                return true;

            count++;
            // delay if we are going to try again
            if (count < retrys) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    // ignore
                }
            }
        }
        return false;
    }

    private static void addArrayToList(List<IStatus> list, IStatus[] a) {
        if (list == null || a == null || a.length == 0)
            return;

        int size = a.length;
        for (int i = 0; i < size; i++)
            list.add(a[i]);
    }

}