blob: 8e45da30ba77edad20993aee1c3b2c2fc41b50c2 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
**********************************************************************/
package org.eclipse.core.internal.resources;
import org.eclipse.core.internal.localstore.*;
import org.eclipse.core.internal.properties.PropertyManager;
import org.eclipse.core.internal.utils.Assert;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.team.IResourceTree;
import org.eclipse.core.runtime.*;
/**
* @since 2.0
*/
class ResourceTree implements IResourceTree {
MultiStatus status;
boolean isValid = true;
/**
* Constructor for this class.
*/
public ResourceTree(MultiStatus status) {
super();
this.status = status;
}
/**
* 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#addToLocalHistory(IFile)
*/
public void addToLocalHistory(IFile file) {
Assert.isLegal(isValid);
if (!file.exists())
return;
IPath path = file.getLocation();
if (path == null || !path.toFile().exists())
return;
long lastModified = internalComputeTimestamp(path.toOSString());
((Resource) file).getLocalManager().getHistoryStore().addState(file.getFullPath(), path, lastModified, false);
}
/**
* @see IResourceTree#movedFile(IFile, IFile)
*/
public void movedFile(IFile source, IFile destination) {
Assert.isLegal(isValid);
// 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 = Policy.bind("resources.mustNotExist", destination.getFullPath().toString()); //$NON-NLS-1$
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.
PropertyManager propertyManager = ((Resource) source).getPropertyManager();
try {
propertyManager.copy(source, destination, IResource.DEPTH_ZERO);
propertyManager.deleteProperties(source, IResource.DEPTH_ZERO);
} catch (CoreException e) {
String message = Policy.bind("resources.errorPropertiesMove", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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, false);
} catch (CoreException e) {
String message = Policy.bind("resources.errorMoving", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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 = Policy.bind("resources.errorMarkersDelete", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
failed(status);
}
}
/**
* @see IResourceTree#movedFolderSubtree(IFolder, IFolder)
*/
public void movedFolderSubtree(IFolder source, IFolder destination) {
Assert.isLegal(isValid);
// 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 = Policy.bind("resources.mustNotExist", destination.getFullPath().toString()); //$NON-NLS-1$
IStatus status= new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
failed(status);
return;
}
// Move the folder properties.
int depth = IResource.DEPTH_INFINITE;
PropertyManager propertyManager = ((Resource) source).getPropertyManager();
try {
propertyManager.copy(source, destination, depth);
propertyManager.deleteProperties(source, depth);
} catch (CoreException e) {
String message = Policy.bind("resources.errorPropertiesMove", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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, false);
} catch (CoreException e) {
String message = Policy.bind("resources.errorMoving", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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 = Policy.bind("resources.errorMarkersDelete", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
failed(status);
}
}
/**
* @see IResourceTree#movedProject(IProject, IProject)
*/
public boolean movedProjectSubtree(IProject project, IProjectDescription destDescription) {
Assert.isLegal(isValid);
// 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());
IProjectDescription srcDescription = source.internalGetDescription();
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 = Policy.bind("resources.mustNotExist", destination.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message);
failed(status);
return false;
}
// Rename the project metadata area. Close the property store so bogus values
// aren't copied to the destination.
// FIXME: do we need to do this?
try {
source.getPropertyManager().closePropertyStore(source);
} catch (CoreException e) {
String message = Policy.bind("properties.couldNotClose", source.getFullPath().toString()); //$NON-NLS-1$
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);
}
java.io.File oldMetaArea = workspace.getMetaArea().locationFor(source).toFile();
java.io.File newMetaArea = workspace.getMetaArea().locationFor(destination).toFile();
try{
source.getLocalManager().getStore().move(oldMetaArea, newMetaArea, false, new NullProgressMonitor());
} catch (CoreException e) {
String message = Policy.bind("resources.moveMeta", oldMetaArea.toString(), newMetaArea.toString()); //$NON-NLS-1$
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, false, true);
} catch (CoreException e) {
String message = Policy.bind("resources.errorMoving", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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 the natures and builders on the destination project.
ProjectInfo info = (ProjectInfo) destination.getResourceInfo(false, true);
info.clearNatures();
info.clearBuilders();
// Generate marker deltas.
try {
workspace.getMarkerManager().moved(source, destination, depth);
} catch (CoreException e) {
String message = Policy.bind("resources.errorMarkersMove", source.getFullPath().toString(), destination.getFullPath().toString()); //$NON-NLS-1$
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);
}
}
// Set the new project description on the destination project.
try {
destination.internalSetDescription(destDescription, true);
destination.writeDescription(IResource.FORCE);
} catch (CoreException e) {
String message = Policy.bind("resources.projectDesc"); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e);
failed(status);
}
// If the locations are the not the same then make sure the new location is written to disk.
// (or the old one removed)
if (srcDescription.getLocation() == null) {
if (destDescription.getLocation() != null) {
try {
workspace.getMetaArea().writeLocation(destination);
} catch (CoreException e) {
failed(e.getStatus());
}
}
} else {
if (!srcDescription.getLocation().equals(destDescription.getLocation())) {
try {
workspace.getMetaArea().writeLocation(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 = Policy.bind("resources.errorRefresh", destination.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.ERROR, destination.getFullPath(), message, e);
failed(status);
return false;
}
return true;
}
/**
* Returns the status object held onto by this resource tree.
*/
protected IStatus getStatus() {
return status;
}
/**
* @see IResourceTree#getTimestamp
*/
public long getTimestamp(IFile file) {
Assert.isLegal(isValid);
if (!file.exists())
return NULL_TIMESTAMP;
ResourceInfo info = ((File) file).getResourceInfo(false, false);
return info == null ? NULL_TIMESTAMP : info.getLocalSyncInfo();
}
/**
* @see IResourceTree#deletedFile(IFile)
*/
public void deletedFile(IFile file) {
Assert.isLegal(isValid);
// 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 = Policy.bind("resources.errorDeleting", file.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, file.getFullPath(), message, e);
failed(status);
}
}
/**
* @see IResourceTree#deletedFolder(IFolder)
*/
public void deletedFolder(IFolder folder) {
Assert.isLegal(isValid);
// 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 = Policy.bind("resources.errorDeleting", folder.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, folder.getFullPath(), message, e);
failed(status);
}
}
/**
* @see IResourceTree#deletedProject(IProject)
*/
public void deletedProject(IProject target) {
Assert.isLegal(isValid);
// Do nothing if the resource doesn't exist.
if (!target.exists())
return;
Project project = (Project) target;
Workspace workspace = (Workspace) project.getWorkspace();
// Delete properties, generate marker deltas, and remove the node from the workspace tree.
try {
project.deleteResource(false, null);
} catch (CoreException e) {
String message = Policy.bind("resources.errorDeleting", project.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, project.getFullPath(), message, e);
// log the status but don't return until we try and delete the rest of the project info
failed(status);
}
// Delete the project metadata.
try {
workspace.getMetaArea().delete(project);
} catch (CoreException e) {
String message = Policy.bind("resources.deleteMeta", project.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_METADATA, project.getFullPath(), message, e);
// log the status but don't return until we try and delete the rest of the project info
failed(status);
}
// Clear the history store.
try {
project.clearHistory(null);
} catch (CoreException e) {
String message = Policy.bind("history.problemsRemoving", project.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, e);
failed(status);
}
}
/**
* This operation has failed for the given reason. Add it to this
* resource tree's status.
*/
public void failed(IStatus reason) {
Assert.isLegal(isValid);
status.add(reason);
}
/**
* 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());
}
/**
* Return <code>true</code> if there is a change in the content area for the project.
*/
private boolean isContentChange(IProject project, IProjectDescription destinationDescription) {
IProjectDescription sourceDescription = ((Project) project).internalGetDescription();
if (sourceDescription.getLocation() == null || destinationDescription.getLocation() == null)
return true;
return !sourceDescription.getLocation().equals(destinationDescription.getLocation());
}
/**
* Returns <code>true</code> if we are doing a change in the case of the project name.
*/
private boolean isCaseChange(IProject project, IProjectDescription description) {
return !project.getName().equals(description.getName()) && project.getName().equalsIgnoreCase(description.getName());
}
/**
* @see IResourceTree#isSynchronized(IResource, int)
*/
public boolean isSynchronized(IResource resource, int depth) {
Assert.isLegal(isValid);
UnifiedTree tree = new UnifiedTree(resource);
String message = Policy.bind("resources.errorRefresh", resource.getFullPath().toString()); //$NON-NLS-1$
// FIXME: this visitor does too much work because it collects statuses for all
// children who are out of sync. We could optimize by returning early when discovering
// the first out of sync child.
CollectSyncStatusVisitor visitor = new CollectSyncStatusVisitor(message, new NullProgressMonitor());
try {
tree.accept(visitor, depth);
} catch (CoreException e) {
IStatus status = new ResourceStatus(IResourceStatus.FAILED_READ_LOCAL, resource.getFullPath(), message, e);
failed(status);
}
return !visitor.resourcesChanged();
}
/**
* @see IResourceTree#computeTimestamp(IFile)
*/
public long computeTimestamp(IFile file) {
Assert.isLegal(isValid);
if (!file.getProject().exists())
return NULL_TIMESTAMP;
return internalComputeTimestamp(file.getLocation().toOSString());
}
/**
* Return the timestamp of the file at the given location.
*/
protected long internalComputeTimestamp(String location) {
return CoreFileSystemLibrary.getLastModified(location);
}
/**
* @see IResourceTree#standardDeleteFile(IFile, int, IProgressMonitor)
*/
public void standardDeleteFile(IFile file, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
internalDeleteFile(file, updateFlags, monitor);
}
/**
* Helper method for #standardDeleteFile. Returns a boolean indicating whether or
* not the delete was successful.
*/
private boolean internalDeleteFile(IFile file, int updateFlags, IProgressMonitor monitor) {
try {
String message = Policy.bind("resources.deleting", file.getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
// Do nothing if the file doesn't exist in the workspace.
if (!file.exists()) {
// Indicate that the delete was successful.
return true;
}
// If the file doesn't exist on disk then signal to the workspace to delete the
// file and return.
java.io.File fileOnDisk = file.getLocation().toFile();
if (!fileOnDisk.exists()) {
deletedFile(file);
// Indicate that the delete was successful.
return true;
}
boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
boolean force = (updateFlags & 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 && file.getLocation().toFile().exists()) {
message = Policy.bind("localstore.resourceIsOutOfSync", file.getFullPath().toString()); //$NON-NLS-1$
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.
boolean success = fileOnDisk.delete();
monitor.worked(Policy.totalWork/4);
// If the file was successfully deleted from the file system the
// workspace tree should be updated accordingly. Otherwise
// we need to signal that a problem occurred.
if (success) {
deletedFile(file);
// Indicate that the delete was successful.
return true;
} else {
message = Policy.bind("resources.couldnotDelete", file.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message);
failed(status);
// Indicate that the delete was unsuccessful.
return false;
}
} finally {
monitor.done();
}
}
/**
* @see IResourceTree#standardDeleteFolder(IFolder, int, IProgressMonitor)
*/
public void standardDeleteFolder(IFolder folder, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
try {
String message = Policy.bind("resources.deleting", folder.getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
// Do nothing if the folder doesn't exist in the workspace.
if (!folder.exists())
return;
// If the folder doesn't exist on disk then update the tree and return.
java.io.File folderOnDisk = folder.getLocation().toFile();
if (!folderOnDisk.exists()) {
deletedFolder(folder);
return;
}
// 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.
boolean force = (updateFlags & IResource.FORCE) != 0;
if (!force && !isSynchronized(folder, IResource.DEPTH_INFINITE)) {
// we are not in sync and force is false so delete via best effort
boolean deletedChildren = internalDeleteFolder(folder, updateFlags, monitor);
if (deletedChildren) {
deletedFolder(folder);
} else {
message = Policy.bind("resources.couldnotDelete", folder.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, folder.getFullPath(), message);
failed(status);
}
return;
}
// Add the contents of the files to the local history if so requested by the user.
boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
if (keepHistory)
addToLocalHistory(folder, IResource.DEPTH_INFINITE);
// If the folder was successfully deleted from the file system the
// workspace tree should be updated accordingly. Otherwise
// we need to signal that a problem occurred.
boolean success = Workspace.clear(folder.getLocation().toFile());
if (success) {
deletedFolder(folder);
} else {
message = Policy.bind("resources.couldnotDelete", folder.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, folder.getFullPath(), message);
failed(status);
}
} finally {
monitor.done();
}
}
/**
* Add this resource and all child files to the local history. Only adds content for
* resources of type <code>IResource.FILE</code>.
*/
private void addToLocalHistory(IResource root, int depth) {
IResourceVisitor visitor = new IResourceVisitor() {
public boolean visit(IResource resource) throws CoreException {
if (resource.getType() == IResource.FILE)
addToLocalHistory((IFile) resource);
return true;
}
};
try {
root.accept(visitor, depth, false);
} catch (CoreException e) {
// We want to ignore any exceptions thrown by the history store because
// they aren't enough to fail the operation as a whole.
}
}
/**
* 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 updateFlags, IProgressMonitor monitor) {
// Recursively delete each member of the folder.
IResource[] members = null;
try {
members = folder.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
} catch (CoreException e) {
String message = Policy.bind("resources.errorMembers", folder.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, folder.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:
deletedChildren &= internalDeleteFile((IFile) child, updateFlags, Policy.subMonitorFor(monitor, Policy.totalWork/members.length));
break;
case IResource.FOLDER:
deletedChildren &= internalDeleteFolder((IFolder) child, updateFlags, Policy.subMonitorFor(monitor, Policy.totalWork/members.length));
break;
}
}
// 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;
}
// Try to delete the folder from the local file system. This will fail
// if the folder is not empty. No need to check the force flag since this is
// an internal method and force is always false when we are here.
java.io.File folderOnDisk = folder.getLocation().toFile();
boolean success = folderOnDisk.delete();
if (!success && !folderOnDisk.exists()) {
// Indicate that the delete was successful.
success = true;
}
if (success) {
deletedFolder(folder);
// Indicate that the delete was successful.
return true;
} else {
String message = Policy.bind("resources.couldnotDelete", folder.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, folder.getFullPath(), message);
failed(status);
// Indicate that the delete was unsuccessful.
return false;
}
}
/**
* @see IResourceTree#standardDeleteProject(IProject, int, IProgressMonitor)
*/
public void standardDeleteProject(IProject project, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
try {
String message = Policy.bind("resources.deleting", project.getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
// Do nothing if the project doesn't exist in the workspace tree.
if (!project.exists())
return;
boolean alwaysDeleteContent = (updateFlags & IResource.ALWAYS_DELETE_PROJECT_CONTENT) != 0;
// don't take force into account if we are always deleting the content
boolean force = alwaysDeleteContent ? true : (updateFlags & IResource.FORCE) != 0;
boolean neverDeleteContent = (updateFlags & IResource.NEVER_DELETE_PROJECT_CONTENT) != 0;
boolean success = true;
// Delete project content. Don't do anything if the user specified explicitly
// not to delete the project content.
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, updateFlags, monitor);
if (success) {
deletedProject(project);
} else {
message = Policy.bind("resources.couldnotDelete", project.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
failed(status);
}
return;
}
// If the content area is in the default location then delete the directory and all its
// children. If it is specified by the user then leave the directory itself but delete the children.
java.io.File root = project.getLocation().toFile();
IProjectDescription description = ((Project) project).internalGetDescription();
if (description == null || description.getLocation() == null) {
success = Workspace.clear(root);
} else {
success = true;
String[] list = root.list();
// for some unknown reason, list() can return null.
// Just skip the children If it does.
if (list != null)
for (int i = 0; i < list.length; i++)
success &= Workspace.clear(new java.io.File(root, list[i]));
}
}
// deleting project content is 75% of the work
monitor.worked(Policy.totalWork*3/4);
// Signal that the workspace tree should be updated that the project
// has been deleted.
if (success) {
deletedProject(project);
} else {
message = Policy.bind("localstore.couldnotDelete", project.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
failed(status);
}
} finally {
monitor.done();
}
}
/**
* 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, IProjectDescription destDescription, int updateFlags, IProgressMonitor monitor) throws CoreException {
try {
IProjectDescription srcDescription = source.getDescription();
// If the locations are the same (and non-default) then there is nothing to do.
if (srcDescription.getLocation() != null && srcDescription.getLocation().equals(destDescription))
return;
IPath srcLocation = source.getLocation();
IPath destLocation = destDescription.getLocation();
// Use the default area if necessary for the destination. The source project
// should already have a location assigned to it.
if (destLocation == null)
destLocation = Platform.getLocation().append(destDescription.getName());
// Move the contents on disk.
moveInFileSystem(srcLocation.toFile(), destLocation.toFile(), updateFlags);
} finally {
monitor.done();
}
}
/**
* @see IResourceTree#standardMoveFile(IFile, IFile, int, IProgressMonitor)
*/
public void standardMoveFile(IFile source, IFile destination, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
try {
String message = Policy.bind("resources.moving", source.getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
// These pre-conditions should all be ok but just in case...
if (!source.exists() || destination.exists() || !destination.getParent().isAccessible())
throw new IllegalArgumentException();
boolean force = (updateFlags & IResource.FORCE) != 0;
boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 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 && !source.getLocation().toFile().exists()) {
message = Policy.bind("localstore.resourceIsOutOfSync", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
failed(status);
return;
} else {
boolean inSync = isSynchronized(source, IResource.DEPTH_ZERO);
if (!inSync) {
message = Policy.bind("localstore.resourceIsOutOfSync", source.getFullPath().toString()); //$NON-NLS-1$
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);
java.io.File sourceFile = source.getLocation().toFile();
java.io.File destFile = destination.getLocation().toFile();
// 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.
try {
moveInFileSystem(sourceFile, destFile, updateFlags);
} catch (CoreException e) {
message = Policy.bind("localstore.couldnotMove", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.ERROR, source.getFullPath(), message, e);
failed(status);
return;
}
movedFile(source, destination);
updateMovedFileTimestamp(destination, computeTimestamp(destination));
monitor.worked(Policy.totalWork/4);
return;
} finally {
monitor.done();
}
}
/**
* @see IResourceTree#standardMoveFolder(IFolder, IFolder, int, IProgressMonitor)
*/
public void standardMoveFolder(IFolder source, IFolder destination, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
try {
String message = Policy.bind("resources.moving", source.getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
// 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 = (updateFlags & IResource.FORCE) != 0;
if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
message = Policy.bind("localstore.resourceIsOutOfSync", source.getFullPath().toString());//$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.ERROR, source.getFullPath(), message);
failed(status);
return;
}
// keep history
boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
if (keepHistory)
addToLocalHistory(source, IResource.DEPTH_INFINITE);
// 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.
try {
moveInFileSystem(source.getLocation().toFile(), destination.getLocation().toFile(), updateFlags);
} catch (CoreException e) {
message = Policy.bind("resources.errorMove"); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_LOCAL, destination.getFullPath(), message, e);
failed(status);
return;
}
boolean success = destination.getLocation().toFile().exists();
success &= !source.getLocation().toFile().exists();
if (success) {
movedFolderSubtree(source, destination);
updateTimestamps(destination);
} else {
message = Policy.bind("localstore.couldNotCreateFolder", destination.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_LOCAL, destination.getFullPath(), message);
failed(status);
}
} finally {
monitor.done();
}
}
/**
* 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) {
IResourceVisitor visitor = new IResourceVisitor() {
public boolean visit(IResource resource) {
if (resource.getType() == IResource.FILE) {
IFile file = (IFile) resource;
long timestamp = computeTimestamp(file);
updateMovedFileTimestamp(file, timestamp);
}
return true;
}
};
try {
root.accept(visitor, IResource.DEPTH_INFINITE, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
} catch(CoreException e) {
// No exception should be thrown.
}
}
/**
* Does a best-effort delete on this resource and all its children.
*/
private boolean internalDeleteProject(IProject project, int updateFlags, 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 = Policy.bind("resources.errorMembers", project.getFullPath().toString()); //$NON-NLS-1$
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:
if (child.getName().equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
// ignore the .project file for now and delete it last
} else {
deletedChildren &= internalDeleteFile((IFile) child, updateFlags, Policy.subMonitorFor(monitor, Policy.totalWork/members.length));
}
break;
case IResource.FOLDER:
deletedChildren &= internalDeleteFolder((IFolder) child, updateFlags, Policy.subMonitorFor(monitor, Policy.totalWork/members.length));
break;
}
}
// 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) {
IResource file = project.findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
if (file == null) {
// For some reason the .project file doesn't exist, so continue with the project
// deletion and pretend we deleted it already.
} else {
if (file.getType() != IResource.FILE) {
// We should never get here since the only reason we skipped it above was because
// it was a file named .project.
String message = Policy.bind("resources.couldnotDelete", file.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message);
failed(status);
// Indicate that the delete was unsuccessful.
return false;
} else {
boolean deletedProjectFile = internalDeleteFile((IFile) file, updateFlags, Policy.monitorFor(null));
if (!deletedProjectFile) {
String message = Policy.bind("resources.couldnotDelete", file.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message);
failed(status);
// Indicate that the delete was unsuccessful.
return false;
}
}
}
} else {
// Indicate that the delete was unsuccessful.
return false;
}
// If the content area is in the default location then delete the directory and all its
// children. If it is specified by the user then leave the directory itself but delete the children.
// No need to check the force flag since this is an internal method and by the time we
// get here we know that force is false.
java.io.File root = project.getLocation().toFile();
IProjectDescription description = ((Project) project).internalGetDescription();
// If we have a user-defined location delete the directory, otherwise just see if its empty
boolean success;
if (description == null || description.getLocation() == null) {
success = root.delete();
if (!success && !root.exists()) {
success = true;
}
} else {
String[] children = root.list();
success = children == null || children.length == 0;
}
if (success) {
deletedProject(project);
// Indicate that the delete was successful.
return true;
} else {
String message = Policy.bind("resources.couldnotDelete", project.getLocation().toOSString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message);
failed(status);
// Indicate that the delete was unsuccessful.
return false;
}
}
/**
* @see IResourceTree#standardMoveProject(IProject, IProjectDescription, int, IProgressMonitor)
*/
public void standardMoveProject(IProject source, IProjectDescription description, int updateFlags, IProgressMonitor monitor) {
Assert.isLegal(isValid);
try {
String message = Policy.bind("resources.moving", source.getFullPath().toString()); //$NON-NLS-1$
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 = (updateFlags & IResource.FORCE) != 0;
if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) {
// FIXME: make this a best effort move?
message = Policy.bind("localstore.resourceIsOutOfSync", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message);
failed(status);
return;
}
// Move the project content in the local file system.
try {
moveProjectContent(source, description, updateFlags, Policy.subMonitorFor(monitor, Policy.totalWork*3/4));
} catch (CoreException e) {
message = Policy.bind("localstore.couldnotMove", source.getFullPath().toString()); //$NON-NLS-1$
IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
failed(status);
return;
}
// 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);
updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()));
} finally {
monitor.done();
}
}
/**
* Move the contents of the specified file from the source location to the destination location.
* If the source points to a directory then move that directory and all its contents.
*
* <code>IResource.FORCE</code> is the only valid flag.
*/
private void moveInFileSystem(java.io.File source, java.io.File destination, int updateFlags) throws CoreException {
Assert.isLegal(isValid);
FileSystemStore store = ((Resource) ResourcesPlugin.getWorkspace().getRoot()).getLocalManager().getStore();
boolean force = (updateFlags & IResource.FORCE) != 0;
store.move(source, destination, force, Policy.monitorFor(null));
}
/**
* @see IResourceTree#updateMovedFileTimestamp(IFile, long)
*/
public void updateMovedFileTimestamp(IFile file, long timestamp) {
Assert.isLegal(isValid);
// 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.
((Resource) file).getLocalManager().updateLocalSync(info, timestamp, true);
}
}