blob: 03136008562cbf0f70c024639271992157b1a35b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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 java.net.MalformedURLException;
import java.net.URL;
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.IMoveDeleteHook;
import org.eclipse.core.runtime.*;
public abstract class Resource extends PlatformObject implements IResource, ICoreConstants, Cloneable {
/* package */ IPath path;
/* package */ Workspace workspace;
protected Resource(IPath path, Workspace workspace) {
this.path = path.removeTrailingSeparator();
this.workspace = workspace;
}
/**
* @see IResource#accept(IResourceVisitor)
*/
public void accept(IResourceVisitor visitor) throws CoreException {
// forward to central method
accept(visitor, IResource.DEPTH_INFINITE, 0);
}
/**
* @see IResource#accept(IResourceVisitor, int, boolean)
*/
public void accept(IResourceVisitor visitor, int depth, boolean includePhantoms) throws CoreException {
// forward to central method
accept(visitor, depth, includePhantoms ? IContainer.INCLUDE_PHANTOMS : 0);
}
/*
* @see IResource#accept
*/
public void accept(IResourceVisitor visitor, int depth, int memberFlags) throws CoreException {
final boolean includePhantoms = (memberFlags & IContainer.INCLUDE_PHANTOMS) != 0;
ResourceInfo info = getResourceInfo(includePhantoms, false);
checkExists(getFlags(info), true);
if (!visitor.visit(this) || depth == DEPTH_ZERO)
return;
// get the info again because it might have been changed by the visitor
info = getResourceInfo(includePhantoms, false);
if (info == null)
return;
// thread safety: (cache the type to avoid changes -- we might not be inside an operation)
int type = info.getType();
if (type == FILE)
return;
if (depth == DEPTH_ONE)
depth = DEPTH_ZERO;
// if we had a gender change we need to fix up the resource before asking for its members
IContainer resource = getType() != type ? (IContainer) workspace.newResource(getFullPath(), type) : (IContainer) this;
IResource[] members = resource.members(memberFlags);
for (int i = 0; i < members.length; i++)
members[i].accept(visitor, depth, memberFlags);
}
protected void assertCopyRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
IStatus status = checkCopyRequirements(destination, destinationType, updateFlags);
if (!status.isOK()) {
// this assert is ok because the error cases generated by the
// check method above indicate assertion conditions.
Assert.isTrue(false, status.getChildren()[0].getMessage());
}
}
protected void assertLinkRequirements(IPath localLocation, int updateFlags) throws CoreException {
checkDoesNotExist(getFlags(getResourceInfo(false, false)), true);
Container parent = (Container) getParent();
if (parent == null || parent.getType() != IResource.PROJECT) {
String msg = Policy.bind("links.parentNotProject", getFullPath().toString());//$NON-NLS-1$
Assert.isLegal(false, msg);
}
parent.checkAccessible(getFlags(parent.getResourceInfo(false, false)));
boolean allowMissingLocal = (updateFlags & IResource.ALLOW_MISSING_LOCAL) != 0;
if (localLocation.isAbsolute()) {
java.io.File localFile = localLocation.toFile();
boolean localExists = localFile.exists();
if (!allowMissingLocal && !localExists) {
String msg = Policy.bind("links.localDoesNotExist", localFile.toString());//$NON-NLS-1$
throw new ResourceException(IResourceStatus.NOT_FOUND_LOCAL, getFullPath(), msg, null);
}
//resource type and file system type must match
if (localExists && ((getType() == IResource.FOLDER) != localFile.isDirectory())) {
String msg = Policy.bind("links.wrongLocalType", getFullPath().toString());//$NON-NLS-1$
throw new ResourceException(IResourceStatus.WRONG_TYPE_LOCAL, getFullPath(), msg, null);
}
} else {
//relative paths are considered to be variable-relative paths
if (!allowMissingLocal) {
String msg = Policy.bind("links.pathNotAbsolute", getFullPath().toString());//$NON-NLS-1$
throw new ResourceException(IResourceStatus.VARIABLE_NOT_DEFINED, getFullPath(), msg, null);
}
}
//check nature veto
String[] natureIds = ((Project)getProject()).internalGetDescription().getNatureIds();
IStatus result = workspace.getNatureManager().validateLinkCreation(natureIds);
if (!result.isOK())
throw new ResourceException(result);
//check team provider veto
if (getType() == IResource.FILE)
result = workspace.getTeamHook().validateCreateLink((IFile)this, updateFlags, localLocation);
else
result = workspace.getTeamHook().validateCreateLink((IFolder)this, updateFlags, localLocation);
if (!result.isOK())
throw new ResourceException(result);
}
protected void assertMoveRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
IStatus status = checkMoveRequirements(destination, destinationType, updateFlags);
if (!status.isOK()) {
// this assert is ok because the error cases generated by the
// check method above indicate assertion conditions.
Assert.isTrue(false, status.getChildren()[0].getMessage());
}
}
public void checkAccessible(int flags) throws CoreException {
checkExists(flags, true);
}
/**
* This method reports errors in two different ways. It can throw a
* CoreException or log a status. CoreExceptions are used according
* to the specification of the copy method. Programming errors, that
* would usually be prevented by using an "Assert" code, are reported as
* an IStatus.
* We're doing this way because we have two different methods to copy
* resources: IResource#copy and IWorkspace#copy. The first one gets
* the error and throws its message in an AssertionFailureException. The
* second one just throws a CoreException using the status returned
* by this method.
*
* @see IResource#copy
*/
public IStatus checkCopyRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
String message = Policy.bind("resources.copyNotMet"); //$NON-NLS-1$
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null);
if (destination == null) {
message = Policy.bind("resources.destNotNull"); //$NON-NLS-1$
return new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message);
}
destination = makePathAbsolute(destination);
if (getFullPath().isPrefixOf(destination)) {
message = Policy.bind("resources.destNotSub"); //$NON-NLS-1$
status.add(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
checkValidPath(destination, destinationType);
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_INFINITE);
Resource dest = (Resource) workspace.newResource(destination, destinationType);
dest.checkDoesNotExist();
// ensure we aren't trying to copy a file to a project
if (getType() == IResource.FILE && destinationType == IResource.PROJECT) {
message = Policy.bind("resources.fileToProj"); //$NON-NLS-1$
throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
// ensure we aren't trying to copy a linked resource into a folder
Container parent = (Container)dest.getParent();
boolean isDeepCopy = (updateFlags & IResource.SHALLOW) == 0;
if (isLinked() && !isDeepCopy && (parent == null || parent.getType() != IResource.PROJECT)) {
message = Policy.bind("links.copyNotProject", getFullPath().toString(), destination.toString()); //$NON-NLS-1$
throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
// we can't copy into a closed project
if (destinationType != IResource.PROJECT) {
Project project = (Project) dest.getProject();
info = project.getResourceInfo(false, false);
project.checkAccessible(getFlags(info));
if (!parent.equals(project)) {
info = parent.getResourceInfo(false, false);
parent.checkExists(getFlags(info), true);
}
}
return status.isOK() ? (IStatus) new ResourceStatus(IResourceStatus.OK, Policy.bind("resources.copyMet")) : (IStatus) status; //$NON-NLS-1$
}
/**
* Helper method that considers case insensitive file systems.
*/
protected void checkDoesNotExist() throws CoreException {
// should consider getting the ResourceInfo as a paramenter to reduce tree lookups
//first check the tree for an exact case match
checkDoesNotExist(getFlags(getResourceInfo(false, false)), false);
if (CoreFileSystemLibrary.isCaseSensitive()) {
return;
}
//now look for a matching case variant in the tree
IResource variant = findExistingResourceVariant(getFullPath());
if (variant == null)
return;
String msg = Policy.bind("resources.existsDifferentCase", variant.getFullPath().toString()); //$NON-NLS-1$
throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, variant.getFullPath(), msg, null);
}
/**
* Checks that this resource does not exist.
*
* @exception CoreException if this resource exists
*/
public void checkDoesNotExist(int flags, boolean checkType) throws CoreException {
// See if there is any resource at all. If none then we are happy.
if (!exists(flags, false))
return;
// We know there is something in the tree at this path.
// If we are checking type then go ahead and check the type.
// If there is nothing there of this resource's type, then return.
if ((checkType && !exists(flags, checkType)))
return;
String message = Policy.bind("resources.mustNotExist", getFullPath().toString()); //$NON-NLS-1$
throw new ResourceException(checkType ? IResourceStatus.RESOURCE_EXISTS : IResourceStatus.PATH_OCCUPIED, getFullPath(), message, null);
}
/**
* Checks that this resource exists.
* If checkType is true, the type of this resource and the one in the tree must match.
*
* @exception CoreException if this resource does not exist
*/
public void checkExists(int flags, boolean checkType) throws CoreException {
if (!exists(flags, checkType)) {
String message = Policy.bind("resources.mustExist", getFullPath().toString()); //$NON-NLS-1$
throw new ResourceException(IResourceStatus.RESOURCE_NOT_FOUND, getFullPath(), message, null);
}
}
/**
* Checks that this resource is local to the given depth.
*
* @exception CoreException if this resource is not local
*/
public void checkLocal(int flags, int depth) throws CoreException {
if (!isLocal(flags, depth)) {
String message = Policy.bind("resources.mustBeLocal", getFullPath().toString()); //$NON-NLS-1$
throw new ResourceException(IResourceStatus.RESOURCE_NOT_LOCAL, getFullPath(), message, null);
}
}
/**
* This method reports errors in two different ways. It can throw a
* CoreException or log a status. CoreExceptions are used according
* to the specification of the move method. Programming errors, that
* would usually be prevented by using an "Assert" code, are reported as
* an IStatus.
* We're doing this way because we have two different methods to move
* resources: IResource#move and IWorkspace#move. The first one gets
* the error and throws its message in an AssertionFailureException. The
* second one just throws a CoreException using the status returned
* by this method.
*
* @see IResource#move
*/
protected IStatus checkMoveRequirements(IPath destination, int destinationType, int updateFlags) throws CoreException {
String message = Policy.bind("resources.moveNotMet"); //$NON-NLS-1$
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null);
if (destination == null) {
message = Policy.bind("resources.destNotNull"); //$NON-NLS-1$
return new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message);
}
destination = makePathAbsolute(destination);
if (getFullPath().isPrefixOf(destination)) {
message = Policy.bind("resources.destNotSub"); //$NON-NLS-1$
status.add(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
checkValidPath(destination, destinationType);
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_INFINITE);
Resource dest = (Resource) workspace.newResource(destination, destinationType);
// check if we are only changing case
IResource variant = CoreFileSystemLibrary.isCaseSensitive() ? null : findExistingResourceVariant(destination);
if (variant == null || !this.equals(variant))
dest.checkDoesNotExist();
// ensure we aren't trying to move a file to a project
if (getType() == IResource.FILE && dest.getType() == IResource.PROJECT) {
message = Policy.bind("resources.fileToProj"); //$NON-NLS-1$
throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
// ensure we aren't trying to shallow move a linked resource into a folder
Container parent = (Container)dest.getParent();
boolean isDeepMove = (updateFlags & IResource.SHALLOW) == 0;
if (!isDeepMove && isLinked() && (parent == null || parent.getType() != IResource.PROJECT)) {
message = Policy.bind("links.moveNotProject", getFullPath().toString(), destination.toString()); //$NON-NLS-1$
throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
// we can't move into a closed project
if (destinationType != IResource.PROJECT) {
Project project = (Project) dest.getProject();
info = project.getResourceInfo(false, false);
project.checkAccessible(getFlags(info));
if (!parent.equals(project)) {
info = parent.getResourceInfo(false, false);
parent.checkExists(getFlags(info), true);
}
}
return status.isOK() ? (IStatus) new ResourceStatus(IResourceStatus.OK, Policy.bind("resources.moveMet")) : (IStatus) status; //$NON-NLS-1$
}
/**
* Checks that the supplied path is valid according to Workspace.validatePath().
*
* @exception CoreException if the path is not valid
*/
public void checkValidPath(IPath path, int type) throws CoreException {
IStatus result = workspace.validatePath(path.toString(), type);
if (!result.isOK())
throw new ResourceException(result);
}
/**
* @see IResource
*/
public void clearHistory(IProgressMonitor monitor) throws CoreException {
getLocalManager().getHistoryStore().removeAll(this);
}
public void convertToPhantom() throws CoreException {
ResourceInfo info = getResourceInfo(false, true);
if (info == null || isPhantom(getFlags(info)))
return;
info.clearSessionProperties();
info.set(M_PHANTOM);
getLocalManager().updateLocalSync(info, I_NULL_SYNC_INFO);
info.setModificationStamp(IResource.NULL_STAMP);
// should already be done by the #deleteResource call but left in
// just to be safe and for code clarity.
info.setMarkers(null);
}
/*
* Used when a folder is to be copied to a project.
* @see IResource#copy
*/
public void copy(IProjectDescription destDesc, int updateFlags, IProgressMonitor monitor) throws CoreException {
// FIXME - funnel through a central method
// FIXME - ensure source is a project
Assert.isNotNull(destDesc);
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.copying", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
// The following assert method throws CoreExceptions as stated in the IResource.copy API
// and assert for programming errors. See checkCopyRequirements for more information.
IPath destPath = new Path(destDesc.getName()).makeAbsolute();
assertCopyRequirements(destPath, getType(), updateFlags);
Project destProject = (Project) workspace.getRoot().getProject(destPath.lastSegment());
workspace.beginOperation(true);
// create and open the new project
destProject.create(destDesc, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
destProject.open(Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
// copy the children
// FIXME: fix the progress monitor here...create a sub monitor and do a worked(1) after each child instead
IResource[] children = ((IContainer) this).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
for (int i = 0; i < children.length; i++) {
Resource child = (Resource) children[i];
child.copy(destPath.append(child.getName()), updateFlags, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100 / children.length));
}
// copy over the properties
getPropertyManager().copy(this, destProject, DEPTH_ZERO);
monitor.worked(Policy.opWork * 15 / 100);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/*
* @see IResource#copy
*/
public void copy(IProjectDescription destDesc, boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
copy(destDesc, updateFlags, monitor);
}
/**
* @see IResource#copy
*/
public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
try {
monitor = Policy.monitorFor(monitor);
String message = Policy.bind("resources.copying", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
// The following assert method throws CoreExceptions as stated in the IResource.copy API
// and assert for programming errors. See checkCopyRequirements for more information.
assertCopyRequirements(destination, getType(), updateFlags);
workspace.beginOperation(true);
Resource destResource = workspace.newResource(makePathAbsolute(destination), getType());
getLocalManager().copy(this, destResource, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IResource#copy
*/
public void copy(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
copy(destination, updateFlags, monitor);
}
/**
* Count the number of resources in the tree from this container to the
* specified depth. Include this resource. Include phantoms if
* the phantom boolean is true.
*/
public int countResources(int depth, boolean phantom) throws CoreException {
return workspace.countResources(path, depth, phantom);
}
/**
* @see org.eclipse.core.resources.IFolder#createLink(IPath, int, IProgressMonitor)
* @see org.eclipse.core.resources.IFile#createLink(IPath, int, IProgressMonitor)
*/
public void createLink(IPath localLocation, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.creatingLink", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
checkValidPath(path, FOLDER);
try {
workspace.prepareOperation();
// resolve any variables used in the location path
IPath resolvedLocation = workspace.getPathVariableManager().resolvePath(localLocation);
assertLinkRequirements(resolvedLocation, updateFlags);
workspace.beginOperation(true);
ResourceInfo info = workspace.createResource(this, false);
info.set(M_LINK);
getLocalManager().link(this, resolvedLocation);
monitor.worked(Policy.opWork * 5 / 100);
//save the location in the project description
Project project = (Project)getProject();
project.internalGetDescription().setLinkLocation(getName(),
new LinkDescription(this,localLocation));
project.writeDescription(IResource.NONE);
monitor.worked(Policy.opWork * 5 / 100);
//notify internal infrastructure that link has been created
workspace.creating(this);
//refresh to discover any new resources below this linked location
if (getType() != IResource.FILE)
refreshLocal(DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 90 / 100));
else
monitor.worked(Policy.opWork * 90 / 100);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IResource
*/
public IMarker createMarker(String type) throws CoreException {
Assert.isNotNull(type);
try {
workspace.prepareOperation();
ResourceInfo resourceInfo = getResourceInfo(false, false);
checkAccessible(getFlags(resourceInfo));
workspace.beginOperation(true);
MarkerInfo info = new MarkerInfo();
info.setType(type);
info.setCreationTime(System.currentTimeMillis());
workspace.getMarkerManager().add(this, new MarkerInfo[] { info });
return new Marker(this, info.getId());
} finally {
workspace.endOperation(false, null);
}
}
/**
* @see IResource
*/
public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
delete(force ? IResource.FORCE : IResource.NONE , monitor);
}
/**
* @see IResource
*/
public void delete(int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.deleting", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork*1000);
try {
workspace.prepareOperation();
/* if there is no such resource (including type check) then there is nothing
to delete so just return. */
if (!exists())
return;
workspace.beginOperation(true);
message = Policy.bind("resources.deleteProblem"); //$NON-NLS-1$
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
ResourceTree tree = new ResourceTree(status);
IMoveDeleteHook hook = workspace.getMoveDeleteHook();
switch (getType()) {
case IResource.FILE:
if (!hook.deleteFile(tree, (IFile) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2)))
tree.standardDeleteFile((IFile) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2));
break;
case IResource.FOLDER:
if (!hook.deleteFolder(tree, (IFolder) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2)))
tree.standardDeleteFolder((IFolder) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2));
break;
case IResource.PROJECT:
workspace.deleting((IProject) this);
if (!hook.deleteProject(tree, (IProject) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2)))
tree.standardDeleteProject((IProject) this, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/2));
break;
case IResource.ROOT:
IProject[] projects = ((IWorkspaceRoot) this).getProjects();
for (int i = 0; i < projects.length; i++) {
workspace.deleting(projects[i]);
if (!hook.deleteProject(tree, projects[i], updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/projects.length/2)))
tree.standardDeleteProject(projects[i], updateFlags, Policy.subMonitorFor(monitor, Policy.opWork*1000/projects.length/2));
}
// need to clear out the root info
workspace.getMarkerManager().removeMarkers(this, IResource.DEPTH_ZERO);
getPropertyManager().deleteProperties(this, IResource.DEPTH_ZERO);
getResourceInfo(false, false).clearSessionProperties();
break;
}
// Invalidate the tree for further use by clients.
tree.makeInvalid();
if (!tree.getStatus().isOK())
throw new ResourceException(tree.getStatus());
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork*1000));
}
} finally {
monitor.done();
}
}
/**
* @see IProject and IWorkspaceRoot -- N.B. This is not an IResource method!
*/
public void delete(boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
delete(updateFlags, monitor);
}
/**
* @see IResource
*/
public void deleteMarkers(String type, boolean includeSubtypes, int depth) throws CoreException {
try {
workspace.prepareOperation();
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
workspace.beginOperation(true);
workspace.getMarkerManager().removeMarkers(this, type, includeSubtypes, depth);
} finally {
workspace.endOperation(false, null);
}
}
/**
* This method should be called to delete a resource from the tree because it will also
* delete its properties and markers. If a status object is provided, minor exceptions are
* added, otherwise they are thrown. If major exceptions occur, they are always thrown.
*/
public void deleteResource(boolean convertToPhantom, MultiStatus status) throws CoreException {
// delete properties
CoreException err = null;
try {
getPropertyManager().deleteProperties(this, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
if (status != null)
status.add(e.getStatus());
else
err = e;
}
// remove markers on this resource and its descendents
if (exists())
getMarkerManager().removeMarkers(this, IResource.DEPTH_INFINITE);
// if this is a linked resource, remove the entry from the project description
if (isLinked()) {
//pre-delete notification to internal infrastructure
workspace.deleting(this);
Project project = (Project)getProject();
ProjectDescription description = project.internalGetDescription();
description.setLinkLocation(getName(), null);
project.internalSetDescription(description, true);
project.writeDescription(IResource.FORCE);
}
/* if we are synchronizing, do not delete the resource. Convert it
into a phantom. Actual deletion will happen when we refresh or push. */
if (convertToPhantom && getType() != PROJECT && synchronizing(getResourceInfo(true, false)))
convertToPhantom();
else
workspace.deleteResource(this);
if (err != null)
throw err;
}
/**
* @see IResource#equals
*/
public boolean equals(Object target) {
if (this == target)
return true;
if (!(target instanceof Resource))
return false;
Resource resource = (Resource) target;
return getType() == resource.getType() && path.equals(resource.path) && workspace.equals(resource.workspace);
}
/**
* @see IResource#exists
*/
public boolean exists() {
ResourceInfo info = getResourceInfo(false, false);
return exists(getFlags(info), true);
}
public boolean exists(int flags, boolean checkType) {
return flags != NULL_FLAG && !(checkType && ResourceInfo.getType(flags) != getType());
}
/**
* @see IResource#findMarker
*/
public IMarker findMarker(long id) throws CoreException {
return workspace.getMarkerManager().findMarker(this, id);
}
/**
* @see IResource#findMarkers
*/
public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth) throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
// It might happen that from this point the resource is not accessible anymore.
// But markers have the #exists method that callers can use to check if it is
// still valid.
return workspace.getMarkerManager().findMarkers(this, type, includeSubtypes, depth);
}
protected void fixupAfterMoveSource() throws CoreException {
ResourceInfo info = getResourceInfo(true, true);
//if a linked resource is moved, we need to remove the location info from the .project
if (isLinked()) {
Project project = (Project)getProject();
project.internalGetDescription().setLinkLocation(getName(), null);
project.writeDescription(IResource.NONE);
}
if (!synchronizing(info)) {
workspace.deleteResource(this);
return;
}
info.clearSessionProperties();
info.clear(M_LOCAL_EXISTS);
info.setLocalSyncInfo(I_NULL_SYNC_INFO);
info.set(M_PHANTOM);
info.setModificationStamp(IResource.NULL_STAMP);
info.setMarkers(null);
}
/**
* @see IResource#getFileExtension
*/
public String getFileExtension() {
String name = getName();
int index = name.lastIndexOf('.');
if (index == -1)
return null;
if (index == (name.length() - 1))
return ""; //$NON-NLS-1$
return name.substring(index + 1);
}
public int getFlags(ResourceInfo info) {
return (info == null) ? NULL_FLAG : info.getFlags();
}
/**
* @see IResource#getFullPath
*/
public IPath getFullPath() {
return path;
}
public FileSystemResourceManager getLocalManager() {
return workspace.getFileSystemManager();
}
/**
* @see IResource#getLocation
*/
public IPath getLocation() {
IProject project = getProject();
if (project != null && !project.exists())
return null;
return getLocalManager().locationFor(this);
}
/**
* @see IResource#getLocationURL
*/
public URL getLocationURL() {
IProject project = getProject();
if (project != null && !project.exists())
return null;
try {
return new URL("platform:/resource" + getFullPath()); //$NON-NLS-1$
} catch (MalformedURLException e) {
return null;
}
}
/**
* @see IResource
*/
public IMarker getMarker(long id) {
return new Marker(this, id);
}
protected MarkerManager getMarkerManager() {
return workspace.getMarkerManager();
}
/**
* @see IResource
*/
public long getModificationStamp() {
ResourceInfo info = getResourceInfo(false, false);
return info == null ? IResource.NULL_STAMP : info.getModificationStamp();
}
/**
* @see IResource#getName
*/
public String getName() {
return path.lastSegment();
}
/**
* @see IResource#getParent
*/
public IContainer getParent() {
IPath parent = path.removeLastSegments(1);
if (parent.isRoot() || parent.isEmpty())
return null;
if (parent.segmentCount() == 1)
return workspace.getRoot().getProject(parent.lastSegment());
else
return workspace.getRoot().getFolder(parent);
}
/**
* @see IResource
*/
public String getPersistentProperty(QualifiedName key) throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
return getPropertyManager().getProperty(this, key);
}
/**
* @see IResource#getProject
*/
public IProject getProject() {
return workspace.getRoot().getProject(path.segment(0));
}
/**
* @see IResource#getProjectRelativePath
*/
public IPath getProjectRelativePath() {
return getFullPath().removeFirstSegments(ICoreConstants.PROJECT_SEGMENT_LENGTH);
}
public PropertyManager getPropertyManager() {
return workspace.getPropertyManager();
}
/**
* @see IResource#getRawLocation
*/
public IPath getRawLocation() {
if (isLinked())
return ((Project)getProject()).internalGetDescription().getLinkLocation(getName());
return getLocation();
}
/**
* Returns the resource info. Returns null if the resource doesn't exist.
* If the phantom flag is true, phantom resources are considered.
* If the mutable flag is true, a mutable info is returned.
*/
public ResourceInfo getResourceInfo(boolean phantom, boolean mutable) {
return workspace.getResourceInfo(getFullPath(), phantom, mutable);
}
/**
* @see IResource
*/
public Object getSessionProperty(QualifiedName key) throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
return info.getSessionProperty(key);
}
/**
* @see IResource#getType
*/
public abstract int getType();
public String getTypeString() {
switch (getType()) {
case FILE :
return "L"; //$NON-NLS-1$
case FOLDER :
return "F"; //$NON-NLS-1$
case PROJECT :
return "P"; //$NON-NLS-1$
case ROOT:
return "R"; //$NON-NLS-1$
}
return ""; //$NON-NLS-1$
}
/**
* @see IResource#getWorkspace
*/
public IWorkspace getWorkspace() {
return workspace;
}
public int hashCode() {
// the container may be null if the identified resource
// does not exist so don't bother with it in the hash
return getFullPath().hashCode();
}
/**
* Sets the M_LOCAL_EXISTS flag. Is internal so we don't have
* to begin an operation.
*/
protected void internalSetLocal(boolean flag, int depth) throws CoreException {
ResourceInfo info = getResourceInfo(true, true);
//only make the change if it's not already in desired state
if (info.isSet(M_LOCAL_EXISTS) != flag) {
if (flag && !isPhantom(getFlags(info))) {
info.set(M_LOCAL_EXISTS);
workspace.updateModificationStamp(info);
} else {
info.clear(M_LOCAL_EXISTS);
info.setModificationStamp(IResource.NULL_STAMP);
}
}
if (getType() == IResource.FILE || depth == IResource.DEPTH_ZERO)
return;
if (depth == IResource.DEPTH_ONE)
depth = IResource.DEPTH_ZERO;
IResource[] children = ((IContainer) this).members();
for (int i = 0; i < children.length; i++)
((Resource) children[i]).internalSetLocal(flag, depth);
}
/**
* @see IResource
*/
public boolean isAccessible() {
return exists();
}
/**
* @see IResource
*/
public boolean isLocal(int depth) {
ResourceInfo info = getResourceInfo(false, false);
return isLocal(getFlags(info), depth);
}
public boolean isLocal(int flags, int depth) {
if (getType() == PROJECT)
return flags != NULL_FLAG; // exists
else
return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_LOCAL_EXISTS);
}
/**
* @see IResource
*/
public boolean isPhantom() {
ResourceInfo info = getResourceInfo(true, false);
return isPhantom(getFlags(info));
}
public boolean isPhantom(int flags) {
return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_PHANTOM);
}
/**
* @see IResource
*/
public boolean isReadOnly() {
IPath location = getLocation();
if (location == null)
return false;
return CoreFileSystemLibrary.isReadOnly(location.toOSString());
}
/**
* @see IResource#isSynchronized(int)
*/
public boolean isSynchronized(int depth) {
return getLocalManager().isSynchronized(this, depth);
}
protected IPath makePathAbsolute(IPath target) {
if (target.isAbsolute())
return target;
return getParent().getFullPath().append(target);
}
/*
* @see IResource#move
*/
public void move(IProjectDescription description, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
move(description, updateFlags, monitor);
}
/*
* Used when a folder is to be moved to a project.
* @see IResource#move
*/
public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(description);
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.moving", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
// The following assert method throws CoreExceptions as stated in the IResource.move API
// and assert for programming errors. See checkCopyRequirements for more information.
if (!getName().equals(description.getName())) {
IPath path = Path.ROOT.append(description.getName());
assertMoveRequirements(path, IResource.PROJECT, updateFlags);
}
workspace.beginOperation(true);
message = Policy.bind("resources.moveProblem"); //$NON-NLS-1$
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
ResourceTree tree = new ResourceTree(status);
IMoveDeleteHook hook = workspace.getMoveDeleteHook();
// if there is a name change then we are deleting the source so notify
IProject project = (IProject) this;
if (!getName().equals(description.getName())) {
workspace.changing(project);
workspace.deleting(project);
}
if (!hook.moveProject(tree, project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2)))
tree.standardMoveProject(project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2));
// Invalidate the tree for further use by clients.
tree.makeInvalid();
if (!tree.getStatus().isOK())
throw new ResourceException(tree.getStatus());
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IFolder#move and IFile#move
*/
public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
move(destination, updateFlags, monitor);
}
/**
* @see IResource#move
*/
public void move(IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
move(destination, force ? IResource.FORCE : IResource.NONE, monitor);
}
/**
* @see IResource#move
*/
public void move(IPath path, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.moving", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
// The following assert method throws CoreExceptions as stated in the IResource.move API
// and assert for programming errors. See checkMoveRequirements for more information.
assertMoveRequirements(path, getType(), updateFlags);
path = makePathAbsolute(path);
workspace.beginOperation(true);
message = Policy.bind("resources.moveProblem"); //$NON-NLS-1$
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
ResourceTree tree = new ResourceTree(status);
IMoveDeleteHook hook = workspace.getMoveDeleteHook();
switch (getType()) {
case IResource.FILE:
IResource destination = workspace.getRoot().getFile(path);
if (!hook.moveFile(tree, (IFile) this, (IFile) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2)))
tree.standardMoveFile((IFile) this, (IFile) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2));
break;
case IResource.FOLDER:
destination = workspace.getRoot().getFolder(path);
if (!hook.moveFolder(tree, (IFolder) this, (IFolder) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2)))
tree.standardMoveFolder((IFolder) this, (IFolder) destination, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2));
break;
case IResource.PROJECT:
IProject project = (IProject) this;
// if there is no change in name, there is nothing to do so return.
if (getName().equals(path.lastSegment())) {
return;
}
//we are deleting the source project so notify.
workspace.changing(project);
workspace.deleting(project);
IProjectDescription description = project.getDescription();
description.setName(path.lastSegment());
if (!hook.moveProject(tree, project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2)))
tree.standardMoveProject(project, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork/2));
break;
case IResource.ROOT:
message = Policy.bind("resources.moveRoot"); //$NON-NLS-1$
throw new ResourceException(new ResourceStatus(IResourceStatus.INVALID_VALUE, getFullPath(), message));
}
// Invalidate the tree for further use by clients.
tree.makeInvalid();
if (!tree.getStatus().isOK())
throw new ResourceException(tree.getStatus());
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IResource#refreshLocal
*/
public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = (getType() == ROOT)
? Policy.bind("resources.refreshingRoot") //$NON-NLS-1$
: Policy.bind("resources.refreshing", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
boolean build = false;
try {
workspace.prepareOperation();
if (getType() != ROOT && !getProject().isAccessible())
return;
workspace.beginOperation(true);
build = getLocalManager().refresh(this, depth, Policy.subMonitorFor(monitor, Policy.opWork));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(build, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IResource
*/
public void setLocal(boolean flag, int depth, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.setLocal"); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
workspace.beginOperation(true);
internalSetLocal(flag, depth);
monitor.worked(Policy.opWork);
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* @see IResource
*/
public void setPersistentProperty(QualifiedName key, String value) throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
getPropertyManager().setProperty(this, key, value);
}
/**
* @see IResource
*/
public void setReadOnly(boolean readonly) {
CoreFileSystemLibrary.setReadOnly(getLocation().toOSString(), readonly);
}
/**
* @see IResource
*/
public void setSessionProperty(QualifiedName key, Object value) throws CoreException {
// fetch the info but don't bother making it mutable even though we are going
// to modify it. We don't know whether or not the tree is open and it really doesn't
// matter as the change we are doing does not show up in deltas.
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
info.setSessionProperty(key, value);
}
/**
* Returns true if this resource has the potential to be
* (or have been) synchronized.
*/
public boolean synchronizing(ResourceInfo info) {
return info != null && info.getSyncInfo(false) != null;
}
public String toString() {
return getTypeString() + getFullPath().toString();
}
/**
* @see IResource
*/
public void touch(IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
try {
String message = Policy.bind("resources.touch", getFullPath().toString()); //$NON-NLS-1$
monitor.beginTask(message, Policy.totalWork);
try {
workspace.prepareOperation();
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
workspace.beginOperation(true);
// fake a change by incrementing the content ID
info = getResourceInfo(false, true);
info.incrementContentId();
workspace.updateModificationStamp(info);
monitor.worked(Policy.opWork);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork));
}
} finally {
monitor.done();
}
}
/**
* Helper method for case insensitive file systems. Returns
* an existing resource whose path differs only in case from
* the given path, or null if no such resource exists.
*/
public IResource findExistingResourceVariant(IPath target) {
IPath result = Path.ROOT;
int segmentCount = target.segmentCount();
for (int i = 0; i < segmentCount; i++) {
String[] childNames = workspace.tree.getNamesOfChildren(result);
String name = findVariant(target.segment(i), childNames);
if (name == null)
return null;
result = result.append(name);
}
return workspace.getRoot().findMember(result);
}
/**
* Searches for a variant of the given target in the list,
* that differs only in case. Returns the variant from
* the list if one is found, otherwise returns null.
*/
private String findVariant(String target, String[] list) {
for (int i = 0; i < list.length; i++) {
if (target.equalsIgnoreCase(list[i]))
return list[i];
}
return null;
}
/*
* @see IResource
*/
public boolean isDerived() {
ResourceInfo info = getResourceInfo(false, false);
return isDerived(getFlags(info));
}
/**
* Returns whether the derived flag is set in the given resource info flags.
*
* @param flags resource info flags (bitwuise or of M_* constants)
* @return <code>true</code> if the derived flag is set, and <code>false</code>
* if the derived flag is not set or if the flags are <code>NULL_FLAG</code>
*/
public boolean isDerived(int flags) {
return flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_DERIVED);
}
/**
* @see IResource#isLinked()
*/
public boolean isLinked() {
ResourceInfo info = getResourceInfo(false, false);
return info != null && info.isSet(M_LINK);
}/*
* @see IResource
*/
public void setDerived(boolean isDerived) throws CoreException {
// fetch the info but don't bother making it mutable even though we are going
// to modify it. We don't know whether or not the tree is open and it really doesn't
// matter as the change we are doing does not show up in deltas.
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
// ignore attempts to set derived flag on anything except files and folders
if (info.getType() == FILE || info.getType() == FOLDER) {
if (isDerived) {
info.set(ICoreConstants.M_DERIVED);
} else {
info.clear(ICoreConstants.M_DERIVED);
}
}
}
/*
* @see IResource
*/
public boolean isTeamPrivateMember() {
ResourceInfo info = getResourceInfo(false, false);
return isTeamPrivateMember(getFlags(info));
}
/**
* Returns whether the team private member flag is set in the given resource info flags.
*
* @param flags resource info flags (bitwise or of M_* constants)
* @return <code>true</code> if the team private member flag is set, and
* <code>false</code> if the flag is not set or if the flags are <code>NULL_FLAG</code>
*/
public boolean isTeamPrivateMember(int flags) {
return flags != NULL_FLAG && ResourceInfo.isSet(flags, ICoreConstants.M_TEAM_PRIVATE_MEMBER);
}/*
* @see IResource
*/
public void setTeamPrivateMember(boolean isTeamPrivate) throws CoreException {
// fetch the info but don't bother making it mutable even though we are going
// to modify it. We don't know whether or not the tree is open and it really doesn't
// matter as the change we are doing does not show up in deltas.
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
// ignore attempts to set team private member flag on anything except files and folders
if (info.getType() == FILE || info.getType() == FOLDER) {
if (isTeamPrivate) {
info.set(ICoreConstants.M_TEAM_PRIVATE_MEMBER);
} else {
info.clear(ICoreConstants.M_TEAM_PRIVATE_MEMBER);
}
}
}
}