Package org.eclipse.core.internal.resources

Source Code of org.eclipse.core.internal.resources.ResourceTree

/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.resources;

import java.net.URI;
import org.eclipse.core.filesystem.*;
import org.eclipse.core.internal.localstore.FileSystemResourceManager;
import org.eclipse.core.internal.properties.IPropertyManager;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.team.IResourceTree;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.osgi.util.NLS;

/**
* @since 2.0
*
* Implementation note: Since the move/delete hook involves running third
* party code, the workspace lock is not held.  This means the workspace
* lock must be re-acquired whenever we need to manipulate the workspace
* in any way.  All entry points from third party code back into the tree must
* be done in an acquire/release pair.
*/
class ResourceTree implements IResourceTree {

  private boolean isValid = true;
  private final FileSystemResourceManager localManager;
  /**
   * The lock to acquire when the workspace needs to be manipulated
   */
  private ILock lock;
  private MultiStatus multistatus;
  private int updateFlags;

  /**
   * Constructor for this class.
   */
  public ResourceTree(FileSystemResourceManager localManager, ILock lock, MultiStatus status, int updateFlags) {
    super();
    this.localManager = localManager;
    this.lock = lock;
    this.multistatus = status;
    this.updateFlags = updateFlags;
  }

  /**
   * @see IResourceTree#addToLocalHistory(IFile)
   */
  public void addToLocalHistory(IFile file) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      if (!file.exists())
        return;
      IFileStore store = localManager.getStore(file);
      final IFileInfo fileInfo = store.fetchInfo();
      if (!fileInfo.exists())
        return;
      localManager.getHistoryStore().addState(file.getFullPath(), store, fileInfo, false);
    } finally {
      lock.release();
    }
  }

  private IFileStore computeDestinationStore(IProjectDescription destDescription) throws CoreException {
    URI destLocation = destDescription.getLocationURI();
    // Use the default area if necessary for the destination.
    if (destLocation == null) {
      IPath rootLocation = ResourcesPlugin.getWorkspace().getRoot().getLocation();
      destLocation = rootLocation.append(destDescription.getName()).toFile().toURI();
    }
    return EFS.getStore(destLocation);
  }

  /**
   * @see IResourceTree#computeTimestamp(IFile)
   */
  public long computeTimestamp(IFile file) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      if (!file.getProject().exists())
        return NULL_TIMESTAMP;
      return internalComputeTimestamp(file);
    } finally {
      lock.release();
    }
  }

  /**
   * Copies the local history of source to destination.  Note that if source
   * is an IFolder, it is assumed that the same structure exists under destination
   * and the local history of any IFile under source will be copied to the
   * associated IFile under destination.
   */
  private void copyLocalHistory(IResource source, IResource destination) {
    localManager.getHistoryStore().copyHistory(source, destination, true);
  }

  /**
   * @see IResourceTree#deletedFile(IFile)
   */
  public void deletedFile(IFile file) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the resource doesn't exist.
      if (!file.exists())
        return;
      try {
        // Delete properties, generate marker deltas, and remove the node from the workspace tree.
        ((Resource) file).deleteResource(true, null);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorDeleting, file.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, file.getFullPath(), message, e);
        failed(status);
      }
    } finally {
      lock.release();
    }
  }

  /**
   * @see IResourceTree#deletedFolder(IFolder)
   */
  public void deletedFolder(IFolder folder) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the resource doesn't exist.
      if (!folder.exists())
        return;
      try {
        // Delete properties, generate marker deltas, and remove the node from the workspace tree.
        ((Resource) folder).deleteResource(true, null);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorDeleting, folder.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, folder.getFullPath(), message, e);
        failed(status);
      }
    } finally {
      lock.release();
    }
  }

  /**
   * @see IResourceTree#deletedProject(IProject)
   */
  public void deletedProject(IProject target) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the resource doesn't exist.
      if (!target.exists())
        return;
      // Delete properties, generate marker deltas, and remove the node from the workspace tree.
      try {
        ((Project) target).deleteResource(false, null);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorDeleting, target.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, target.getFullPath(), message, e);
        // log the status but don't return until we try and delete the rest of the project info
        failed(status);
      }
    } finally {
      lock.release();
    }
  }

  /**
   * Makes sure that the destination directory for a project move is unoccupied.
   * Returns true if successful, and false if the move should be aborted
   */
  private boolean ensureDestinationEmpty(IProject source, IFileStore destinationStore, IProgressMonitor monitor) throws CoreException {
    String message;
    //Make sure the destination location is unoccupied
    if (!destinationStore.fetchInfo().exists())
      return true;
    //check for existing children
    if (destinationStore.childNames(EFS.NONE, Policy.subMonitorFor(monitor, 0)).length > 0) {
      //allow case rename to proceed
      if (((Resource) source).getStore().equals(destinationStore))
        return true;
      //fail because the destination is occupied
      message = NLS.bind(Messages.localstore_resourceExists, destinationStore);
      IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, null);
      failed(status);
      return false;
    }
    //delete the destination directory to allow for efficient renaming
    destinationStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, 0));
    return true;
  }

  /**
   * This operation has failed for the given reason. Add it to this
   * resource tree's status.
   */
  public void failed(IStatus reason) {
    Assert.isLegal(isValid);
    multistatus.add(reason);
  }

  /**
   * Returns the status object held onto by this resource tree.
   */
  protected IStatus getStatus() {
    return multistatus;
  }

  /**
   * @see IResourceTree#getTimestamp(IFile)
   */
  public long getTimestamp(IFile file) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      if (!file.exists())
        return NULL_TIMESTAMP;
      ResourceInfo info = ((File) file).getResourceInfo(false, false);
      return info == null ? NULL_TIMESTAMP : info.getLocalSyncInfo();
    } finally {
      lock.release();
    }
  }

  /**
   * Returns the local timestamp for a file.
   *
   * @param file
   * @return The local file system timestamp
   */
  private long internalComputeTimestamp(IFile file) {
    IFileInfo fileInfo = localManager.getStore(file).fetchInfo();
    return fileInfo.exists() ? fileInfo.getLastModified() : NULL_TIMESTAMP;
  }

  /**
   * Helper method for #standardDeleteFile. Returns a boolean indicating whether or
   * not the delete was successful.
   */
  private boolean internalDeleteFile(IFile file, int flags, IProgressMonitor monitor) {
    try {
      String message = NLS.bind(Messages.resources_deleting, file.getFullPath());
      monitor.beginTask(message, Policy.totalWork);
      Policy.checkCanceled(monitor);

      // Do nothing if the file doesn't exist in the workspace.
      if (!file.exists()) {
        // Indicate that the delete was successful.
        return true;
      }
      // Don't delete contents if this is a linked resource
      if (file.isLinked()) {
        deletedFile(file);
        return true;
      }
      // If the file doesn't exist on disk then signal to the workspace to delete the
      // file and return.
      IFileStore fileStore = localManager.getStore(file);
      boolean localExists = fileStore.fetchInfo().exists();
      if (!localExists) {
        deletedFile(file);
        // Indicate that the delete was successful.
        return true;
      }

      boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
      boolean force = (flags & IResource.FORCE) != 0;

      // Add the file to the local history if requested by the user.
      if (keepHistory)
        addToLocalHistory(file);
      monitor.worked(Policy.totalWork / 4);

      // We want to fail if force is false and the file is not synchronized with the
      // local file system.
      if (!force) {
        boolean inSync = isSynchronized(file, IResource.DEPTH_ZERO);
        // only want to fail if the file still exists.
        if (!inSync && localExists) {
          message = NLS.bind(Messages.localstore_resourceIsOutOfSync, file.getFullPath());
          IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, file.getFullPath(), message);
          failed(status);
          // Indicate that the delete was unsuccessful.
          return false;
        }
      }
      monitor.worked(Policy.totalWork / 4);

      // Try to delete the file from the file system.
      try {
        fileStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork / 4));
        // If the file was successfully deleted from the file system the
        // workspace tree should be updated accordingly.
        deletedFile(file);
        // Indicate that the delete was successful.
        return true;
      } catch (CoreException e) {
        message = NLS.bind(Messages.resources_couldnotDelete, fileStore.toString());
        IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message, e);
        failed(status);
      }
      // Indicate that the delete was unsuccessful.
      return false;
    } finally {
      monitor.done();
    }
  }

  /**
   * Helper method for #standardDeleteFolder. Returns a boolean indicating
   * whether or not the deletion of this folder was successful. Does a best effort
   * delete of this resource and its children.
   */
  private boolean internalDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) {
    String message = NLS.bind(Messages.resources_deleting, folder.getFullPath());
    monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
    monitor.subTask(message);
    Policy.checkCanceled(monitor);

    // Do nothing if the folder doesn't exist in the workspace.
    if (!folder.exists())
      return true;

    // Don't delete contents if this is a linked resource
    if (folder.isLinked()) {
      deletedFolder(folder);
      return true;
    }

    // If the folder doesn't exist on disk then update the tree and return.
    IFileStore fileStore = localManager.getStore(folder);
    if (!fileStore.fetchInfo().exists()) {
      deletedFolder(folder);
      return true;
    }

    try {
      //this will delete local and workspace
      localManager.delete(folder, flags, Policy.subMonitorFor(monitor, Policy.totalWork));
    } catch (CoreException ce) {
      message = NLS.bind(Messages.localstore_couldnotDelete, folder.getFullPath());
      MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_DELETE_LOCAL, message, ce);
      if (ce.getStatus() != null)
        status.merge(ce.getStatus());
      failed(status);
      return false;
    }
    return true;
  }

  /**
   * Does a best-effort delete on this resource and all its children.
   */
  private boolean internalDeleteProject(IProject project, int flags, IProgressMonitor monitor) {

    // Recursively delete each member of the project.
    IResource[] members = null;
    try {
      members = project.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
    } catch (CoreException e) {
      String message = NLS.bind(Messages.resources_errorMembers, project.getFullPath());
      IStatus status = new ResourceStatus(IStatus.ERROR, project.getFullPath(), message, e);
      failed(status);
      // Indicate that the delete was unsuccessful.
      return false;
    }
    boolean deletedChildren = true;
    for (int i = 0; i < members.length; i++) {
      IResource child = members[i];
      switch (child.getType()) {
        case IResource.FILE :
          // ignore the .project file for now and delete it last
          if (!IProjectDescription.DESCRIPTION_FILE_NAME.equals(child.getName()))
            deletedChildren &= internalDeleteFile((IFile) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length));
          break;
        case IResource.FOLDER :
          deletedChildren &= internalDeleteFolder((IFolder) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length));
          break;
      }
    }
    IFileStore projectStore = localManager.getStore(project);
    // Check to see if the children were deleted ok. If there was a problem
    // just return as the problem should have been logged by the recursive
    // call to the child.
    if (!deletedChildren)
      // Indicate that the delete was unsuccessful.
      return false;

    //Check if there are any undiscovered children of the project on disk other than description file
    String[] children;
    try {
      children = projectStore.childNames(EFS.NONE, null);
    } catch (CoreException e) {
      //treat failure to access the directory as a non-existent directory
      children = new String[0];
    }
    if (children.length != 1 || !IProjectDescription.DESCRIPTION_FILE_NAME.equals(children[0])) {
      String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, project.getName());
      failed(new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, project.getFullPath(), message));
      return false;
    }

    //Now delete the project description file
    IResource file = project.findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
    if (file == null) {
      //the .project have may have been recreated on disk automatically by snapshot
      IFileStore dotProject = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
      try {
        dotProject.delete(EFS.NONE, null);
      } catch (CoreException e) {
        failed(e.getStatus());
      }
    } else {
      boolean deletedProjectFile = internalDeleteFile((IFile) file, flags, Policy.monitorFor(null));
      if (!deletedProjectFile) {
        String message = NLS.bind(Messages.resources_couldnotDelete, file.getFullPath());
        IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message);
        failed(status);
        // Indicate that the delete was unsuccessful.
        return false;
      }
    }

    //children are deleted, so now delete the parent
    try {
      projectStore.delete(EFS.NONE, null);
      deletedProject(project);
      // Indicate that the delete was successful.
      return true;
    } catch (CoreException e) {
      String message = NLS.bind(Messages.resources_couldnotDelete, projectStore.toString());
      IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, e);
      failed(status);
      // Indicate that the delete was unsuccessful.
      return false;
    }
  }

  /**
   * Return <code>true</code> if there is a change in the content area for the project.
   */
  private boolean isContentChange(IProject project, IProjectDescription destDescription) {
    IProjectDescription srcDescription = ((Project) project).internalGetDescription();
    URI srcLocation = srcDescription.getLocationURI();
    URI destLocation = destDescription.getLocationURI();
    if (srcLocation == null || destLocation == null)
      return true;
    //don't use URIUtil because we want to treat case rename as a content change
    return !srcLocation.equals(destLocation);
  }

  /**
   * Return <code>true</code> if there is a change in the name of the project.
   */
  private boolean isNameChange(IProject project, IProjectDescription description) {
    return !project.getName().equals(description.getName());
  }

  /**
   * @see IResourceTree#isSynchronized(IResource, int)
   */
  public boolean isSynchronized(IResource resource, int depth) {
    try {
      lock.acquire();
      return localManager.isSynchronized(resource, depth);
    } finally {
      lock.release();
    }
  }

  /**
   * The specific operation for which this tree was created has completed and this tree
   * should not be used anymore. Ensure that this is the case by making it invalid. This
   * is checked by all API methods.
   */
  void makeInvalid() {
    this.isValid = false;
  }

  /**
   * @see IResourceTree#movedFile(IFile, IFile)
   */
  public void movedFile(IFile source, IFile destination) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the resource doesn't exist.
      if (!source.exists())
        return;
      // If the destination already exists then we have a problem.
      if (destination.exists()) {
        String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
        // log the status but don't return until we try and move the rest of the resource information.
        failed(status);
      }

      // Move the resource's persistent properties.
      IPropertyManager propertyManager = ((Resource) source).getPropertyManager();
      try {
        propertyManager.copy(source, destination, IResource.DEPTH_ZERO);
        propertyManager.deleteProperties(source, IResource.DEPTH_ZERO);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        // log the status but don't return until we try and move the rest of the resource information.
        failed(status);
      }

      // Move the node in the workspace tree.
      Workspace workspace = (Workspace) source.getWorkspace();
      try {
        workspace.move((Resource) source, destination.getFullPath(), IResource.DEPTH_ZERO, updateFlags, false);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        // log the status but don't return until we try and move the rest of the resource information.
        failed(status);
      }

      // Generate the marker deltas.
      try {
        workspace.getMarkerManager().moved(source, destination, IResource.DEPTH_ZERO);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        failed(status);
      }

      // Copy the local history information
      copyLocalHistory(source, destination);
    } finally {
      lock.release();
    }
  }

  /**
   * @see IResourceTree#movedFolderSubtree(IFolder, IFolder)
   */
  public void movedFolderSubtree(IFolder source, IFolder destination) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the source resource doesn't exist.
      if (!source.exists())
        return;
      // If the destination already exists then we have an error.
      if (destination.exists()) {
        String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
        failed(status);
        return;
      }

      // Move the folder properties.
      int depth = IResource.DEPTH_INFINITE;
      IPropertyManager propertyManager = ((Resource) source).getPropertyManager();
      try {
        propertyManager.copy(source, destination, depth);
        propertyManager.deleteProperties(source, depth);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        // log the status but don't return until we try and move the rest of the resource info
        failed(status);
      }

      // Create the destination node in the tree.
      Workspace workspace = (Workspace) source.getWorkspace();
      try {
        workspace.move((Resource) source, destination.getFullPath(), depth, updateFlags, false);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        // log the status but don't return until we try and move the rest of the resource info
        failed(status);
      }

      // Generate the marker deltas.
      try {
        workspace.getMarkerManager().moved(source, destination, depth);
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        failed(status);
      }

      // Copy the local history for this folder
      copyLocalHistory(source, destination);
    } finally {
      lock.release();
    }
  }

  /**
   * @see IResourceTree#movedProjectSubtree(IProject, IProjectDescription)
   */
  public boolean movedProjectSubtree(IProject project, IProjectDescription destDescription) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the source resource doesn't exist.
      if (!project.exists())
        return true;

      Project source = (Project) project;
      Project destination = (Project) source.getWorkspace().getRoot().getProject(destDescription.getName());
      Workspace workspace = (Workspace) source.getWorkspace();
      int depth = IResource.DEPTH_INFINITE;

      // If the name of the source and destination projects are not the same then
      // rename the meta area and make changes in the tree.
      if (isNameChange(source, destDescription)) {
        if (destination.exists()) {
          String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath());
          IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
          failed(status);
          return false;
        }

        // Rename the project metadata area. Close the property store to flush everything to disk
        try {
          source.getPropertyManager().closePropertyStore(source);
          localManager.getHistoryStore().closeHistoryStore(source);
        } catch (CoreException e) {
          String message = NLS.bind(Messages.properties_couldNotClose, source.getFullPath());
          IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
          // log the status but don't return until we try and move the rest of the resource info
          failed(status);
        }
        final IFileSystem fileSystem = EFS.getLocalFileSystem();
        IFileStore oldMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(source));
        IFileStore newMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(destination));
        try {
          oldMetaArea.move(newMetaArea, EFS.NONE, new NullProgressMonitor());
        } catch (CoreException e) {
          String message = NLS.bind(Messages.resources_moveMeta, oldMetaArea, newMetaArea);
          IStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, destination.getFullPath(), message, e);
          // log the status but don't return until we try and move the rest of the resource info
          failed(status);
        }

        // Move the workspace tree.
        try {
          workspace.move(source, destination.getFullPath(), depth, updateFlags, true);
        } catch (CoreException e) {
          String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath());
          IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
          // log the status but don't return until we try and move the rest of the resource info
          failed(status);
        }

        // Clear stale state on the destination project.
        ((ProjectInfo) destination.getResourceInfo(false, true)).fixupAfterMove();

        // Generate marker deltas.
        try {
          workspace.getMarkerManager().moved(source, destination, depth);
        } catch (CoreException e) {
          String message = NLS.bind(Messages.resources_errorMarkersMove, source.getFullPath(), destination.getFullPath());
          IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
          // log the status but don't return until we try and move the rest of the resource info
          failed(status);
        }
        // Copy the local history
        copyLocalHistory(source, destination);
      }

      // Write the new project description on the destination project.
      try {
        //moving linked resources may have modified the description in memory
        ((ProjectDescription) destDescription).setLinkDescriptions(destination.internalGetDescription().getLinks());
        destination.internalSetDescription(destDescription, true);
        destination.writeDescription(IResource.FORCE);
      } catch (CoreException e) {
        String message = Messages.resources_projectDesc;
        IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e);
        failed(status);
      }

      // write the private project description, including the project location
      try {
        workspace.getMetaArea().writePrivateDescription(destination);
      } catch (CoreException e) {
        failed(e.getStatus());
      }

      // Do a refresh on the destination project to pick up any newly discovered resources
      try {
        destination.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
      } catch (CoreException e) {
        String message = NLS.bind(Messages.resources_errorRefresh, destination.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e);
        failed(status);
        return false;
      }
      return true;
    } finally {
      lock.release();
    }
  }

  /**
   * Helper method for moving the project content. Determines the content location
   * based on the project description. (default location or user defined?)
   */
  private void moveProjectContent(IProject source, IFileStore destStore, int flags, IProgressMonitor monitor) throws CoreException {
    try {
      String message = NLS.bind(Messages.resources_moving, source.getFullPath());
      monitor.beginTask(message, 10);
      IProjectDescription srcDescription = source.getDescription();
      URI srcLocation = srcDescription.getLocationURI();
      // If the locations are the same (and non-default) then there is nothing to do.
      if (srcLocation != null && URIUtil.equals(srcLocation, destStore.toURI()))
        return;

      //If this is a replace, just make sure the destination location exists, and return
      boolean replace = (flags & IResource.REPLACE) != 0;
      if (replace) {
        destStore.mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 10));
        return;
      }

      // Move the contents on disk.
      localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 9));

      //if this is a deep move, move the contents of any linked resources
      if ((flags & IResource.SHALLOW) == 0) {
        IResource[] children = source.members();
        for (int i = 0; i < children.length; i++) {
          if (children[i].isLinked()) {
            message = NLS.bind(Messages.resources_moving, children[i].getFullPath());
            monitor.subTask(message);
            IFileStore linkDestination = destStore.getChild(children[i].getName());
            try {
              localManager.move(children[i], linkDestination, flags, Policy.monitorFor(null));
            } catch (CoreException ce) {
              //log the failure, but keep trying on remaining links
              failed(ce.getStatus());
            }
          }
        }
      }
      monitor.worked(1);
    } finally {
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#standardDeleteFile(IFile, int, IProgressMonitor)
   */
  public void standardDeleteFile(IFile file, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      internalDeleteFile(file, flags, monitor);
    } finally {
      lock.release();
    }
  }

  /**
   * @see IResourceTree#standardDeleteFolder(IFolder, int, IProgressMonitor)
   */
  public void standardDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      internalDeleteFolder(folder, flags, monitor);
    } finally {
      lock.release();
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#standardDeleteProject(IProject, int, IProgressMonitor)
   */
  public void standardDeleteProject(IProject project, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      String message = NLS.bind(Messages.resources_deleting, project.getFullPath());
      monitor.beginTask(message, Policy.totalWork);
      // Do nothing if the project doesn't exist in the workspace tree.
      if (!project.exists())
        return;

      boolean alwaysDeleteContent = (flags & IResource.ALWAYS_DELETE_PROJECT_CONTENT) != 0;
      //force is implied if alwaysDeleteContent is true
      if (alwaysDeleteContent)
        flags |= IResource.FORCE;
      boolean force = (flags & IResource.FORCE) != 0;
      boolean neverDeleteContent = (flags & IResource.NEVER_DELETE_PROJECT_CONTENT) != 0;
      boolean success = true;

      // Delete project content.  Don't do anything if the user specified explicitly asked
      // not to delete the project content or if the project is closed and
      // ALWAYS_DELETE_PROJECT_CONTENT was not specified.
      if (alwaysDeleteContent || (project.isOpen() && !neverDeleteContent)) {
        // Check to see if we are synchronized with the local file system. If we are in sync then
        // we can short circuit this operation and delete all the files on disk, otherwise we have
        // to recursively try and delete them doing best-effort, thus leaving only the ones which
        // were out of sync.
        if (!force && !isSynchronized(project, IResource.DEPTH_INFINITE)) {
          // we are not in sync and force is false so delete via best effort
          success = internalDeleteProject(project, flags, monitor);
          if (!success) {
            IFileStore store = localManager.getStore(project);
            message = NLS.bind(Messages.resources_couldnotDelete, store.toString());
            IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
            failed(status);
          }
          return;
        }

        try {
          IFileStore projectStore = localManager.getStore(project);
          // if the project is open, we must perform a best-effort deletion     
          if (project.isOpen()) {
            //use force because we already checked for synchronization above
            localManager.delete(project, flags & IResource.FORCE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8));
          } else {
            projectStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8));
          }
        } catch (CoreException ce) {
          message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath());
          MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_DELETE_LOCAL, message, ce);
          if (ce.getStatus() != null)
            status.merge(ce.getStatus());
          failed(status);
          return;
        }
      }

      // Signal that the workspace tree should be updated that the project has been deleted.
      if (success)
        deletedProject(project);
      else {
        message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath());
        IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
        failed(status);
      }
    } finally {
      lock.release();
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#standardMoveFile(IFile, IFile, int, IProgressMonitor)
   */
  public void standardMoveFile(IFile source, IFile destination, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      String message = NLS.bind(Messages.resources_moving, source.getFullPath());
      monitor.subTask(message);

      // These pre-conditions should all be ok but just in case...
      if (!source.exists() || destination.exists() || !destination.getParent().isAccessible())
        throw new IllegalArgumentException();

      boolean force = (flags & IResource.FORCE) != 0;
      boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
      boolean isDeep = (flags & IResource.SHALLOW) == 0;

      // If the file is not in sync with the local file system and force is false,
      // then signal that we have an error.
      if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
        message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
        IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
        failed(status);
        return;
      }
      monitor.worked(Policy.totalWork / 4);

      // Add the file contents to the local history if requested by the user. 
      if (keepHistory)
        addToLocalHistory(source);
      monitor.worked(Policy.totalWork / 4);

      //for shallow move of linked resources, nothing needs to be moved in the file system
      if (!isDeep && source.isLinked()) {
        movedFile(source, destination);
        return;
      }

      // If the file was successfully moved in the file system then the workspace
      // tree needs to be updated accordingly. Otherwise signal that we have an error.
      IFileStore destStore = null;
      boolean failedDeletingSource = false;
      try {
        destStore = localManager.getStore(destination);
        //ensure parent of destination exists
        destStore.getParent().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 0));
        localManager.move(source, destStore, flags, monitor);
      } catch (CoreException e) {
        failed(e.getStatus());
        // did the fail occur after copying to the destination?                 
        failedDeletingSource = destStore != null && destStore.fetchInfo().exists();
        // if so, we should proceed
        if (!failedDeletingSource)
          return;
      }
      movedFile(source, destination);
      updateMovedFileTimestamp(destination, internalComputeTimestamp(destination));
      if (failedDeletingSource) {
        //recreate source file to ensure we are not out of sync
        try {
          source.refreshLocal(IResource.DEPTH_INFINITE, null);
        } catch (CoreException e) {
          //ignore secondary failure - we have already logged the main failure
        }
      }
      monitor.worked(Policy.totalWork / 4);
      return;
    } finally {
      lock.release();
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#standardMoveFolder(IFolder, IFolder, int, IProgressMonitor)
   */
  public void standardMoveFolder(IFolder source, IFolder destination, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      String message = NLS.bind(Messages.resources_moving, source.getFullPath());
      monitor.beginTask(message, 100);

      // These pre-conditions should all be ok but just in case...
      if (!source.exists() || destination.exists() || !destination.getParent().isAccessible())
        throw new IllegalArgumentException();

      // Check to see if we are synchronized with the local file system. If we are in sync then we can
      // short circuit this method and do a file system only move. Otherwise we have to recursively
      // try and move all resources, doing it in a best-effort manner.
      boolean force = (flags & IResource.FORCE) != 0;
      if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
        message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message);
        failed(status);
        return;
      }
      monitor.worked(20);

      //for linked resources, nothing needs to be moved in the file system
      boolean isDeep = (flags & IResource.SHALLOW) == 0;
      if (!isDeep && source.isLinked()) {
        movedFolderSubtree(source, destination);
        return;
      }

      // Move the resources in the file system. Only the FORCE flag is valid here so don't
      // have to worry about clearing the KEEP_HISTORY flag.
      IFileStore destStore = null;
      boolean failedDeletingSource = false;
      try {
        destStore = localManager.getStore(destination);
        localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 60));
      } catch (CoreException e) {
        failed(e.getStatus());
        // did the fail occur after copying to the destination?
        failedDeletingSource = destStore != null && destStore.fetchInfo().exists();
        // if so, we should proceed
        if (!failedDeletingSource)
          return;
      }
      movedFolderSubtree(source, destination);
      monitor.worked(20);
      updateTimestamps(destination, isDeep);
      if (failedDeletingSource) {
        //the move could have been partially successful, so refresh to ensure we are in sync
        try {
          source.refreshLocal(IResource.DEPTH_INFINITE, null);
          destination.refreshLocal(IResource.DEPTH_INFINITE, null);
        } catch (CoreException e) {
          //ignore secondary failures -we have already logged main failure
        }
      }
    } finally {
      lock.release();
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#standardMoveProject(IProject, IProjectDescription, int, IProgressMonitor)
   */
  public void standardMoveProject(IProject source, IProjectDescription description, int flags, IProgressMonitor monitor) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      String message = NLS.bind(Messages.resources_moving, source.getFullPath());
      monitor.beginTask(message, Policy.totalWork);

      // Double-check this pre-condition.
      if (!source.isAccessible())
        throw new IllegalArgumentException();

      // If there is nothing to do on disk then signal to make the workspace tree
      // changes.
      if (!isContentChange(source, description)) {
        movedProjectSubtree(source, description);
        return;
      }

      // Check to see if we are synchronized with the local file system.
      boolean force = (flags & IResource.FORCE) != 0;
      if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
        // FIXME: make this a best effort move?
        message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath());
        IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
        failed(status);
        return;
      }

      IFileStore destinationStore;
      try {
        destinationStore = computeDestinationStore(description);
        //destination can be non-empty on replace
        if ((flags & IResource.REPLACE) == 0)
          if (!ensureDestinationEmpty(source, destinationStore, monitor))
            return;
      } catch (CoreException e) {
        //must fail if the destination location cannot be accessd (undefined file system)
        message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        failed(status);
        return;
      }

      // Move the project content in the local file system.
      try {
        moveProjectContent(source, destinationStore, flags, Policy.subMonitorFor(monitor, Policy.totalWork * 3 / 4));
      } catch (CoreException e) {
        message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
        IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
        failed(status);
        //refresh the project because it might have been partially moved
        try {
          source.refreshLocal(IResource.DEPTH_INFINITE, null);
        } catch (CoreException e2) {
          //ignore secondary failures
        }
      }

      // If we got this far the project content has been moved on disk (if necessary)
      // and we need to update the workspace tree.
      movedProjectSubtree(source, description);
      monitor.worked(Policy.totalWork * 1 / 8);

      boolean isDeep = (flags & IResource.SHALLOW) == 0;
      updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()), isDeep);
      monitor.worked(Policy.totalWork * 1 / 8);
    } finally {
      lock.release();
      monitor.done();
    }
  }

  /**
   * @see IResourceTree#updateMovedFileTimestamp(IFile, long)
   */
  public void updateMovedFileTimestamp(IFile file, long timestamp) {
    Assert.isLegal(isValid);
    try {
      lock.acquire();
      // Do nothing if the file doesn't exist in the workspace tree.
      if (!file.exists())
        return;
      // Update the timestamp in the tree.
      ResourceInfo info = ((Resource) file).getResourceInfo(false, true);
      // The info should never be null since we just checked that the resource exists in the tree.
      localManager.updateLocalSync(info, timestamp);
      //remove the linked bit since this resource has been moved in the file system
      info.clear(ICoreConstants.M_LINK);
    } finally {
      lock.release();
    }
  }

  /**
   * Helper method to update all the timestamps in the tree to match
   * those in the file system. Used after a #move.
   */
  private void updateTimestamps(IResource root, final boolean isDeep) {
    IResourceVisitor visitor = new IResourceVisitor() {
      public boolean visit(IResource resource) {
        if (resource.isLinked()) {
          if (isDeep) {
            //clear the linked resource bit, if any
            ResourceInfo info = ((Resource) resource).getResourceInfo(false, true);
            info.clear(ICoreConstants.M_LINK);
          }
          return true;
        }
        //only needed if underlying file system does not preserve timestamps
        //        if (resource.getType() == IResource.FILE) {
        //          IFile file = (IFile) resource;
        //          updateMovedFileTimestamp(file, computeTimestamp(file));
        //        }
        return true;
      }
    };
    try {
      root.accept(visitor, IResource.DEPTH_INFINITE, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
    } catch (CoreException e) {
      // No exception should be thrown.
    }
  }
}
TOP

Related Classes of org.eclipse.core.internal.resources.ResourceTree

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.