| /******************************************************************************* |
| * 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 v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.core.internal.resources; |
| |
| import java.util.*; |
| |
| import org.eclipse.core.internal.utils.Assert; |
| import org.eclipse.core.internal.utils.Policy; |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.runtime.*; |
| |
| public class Project extends Container implements IProject { |
| /** |
| * Used to ensure we don't read the description immediately after writing it. |
| */ |
| private boolean isWritingDescription = false; |
| |
| protected Project(IPath path, Workspace container) { |
| super(path, container); |
| } |
| |
| /* |
| * If the creation boolean is true then this method is being called on project creation. |
| * Otherwise it is being called via #setDescription. The difference is that we don't allow |
| * some description fields to change value after project creation. (e.g. project location) |
| */ |
| protected MultiStatus basicSetDescription(ProjectDescription description) { |
| String message = Policy.bind("resources.projectDesc"); //$NON-NLS-1$ |
| MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_WRITE_METADATA, message, null); |
| ProjectDescription current = (ProjectDescription) internalGetDescription(); |
| current.setComment(description.getComment()); |
| // set the build order before setting the references or the natures |
| current.setBuildSpec(description.getBuildSpec(true)); |
| |
| // set the references before the natures |
| IProject[] oldReferences = current.getReferencedProjects(); |
| IProject[] newReferences = description.getReferencedProjects(); |
| if (!Arrays.equals(oldReferences, newReferences)) { |
| current.setReferencedProjects(description.getReferencedProjects(true)); |
| workspace.flushBuildOrder(); |
| } |
| // the natures last as this may cause recursive calls to setDescription. |
| workspace.getNatureManager().configureNatures(this, current, description, result); |
| return result; |
| } |
| /** |
| * @see IProject#build |
| */ |
| public void build(int kind, String builderName, Map args, IProgressMonitor monitor) throws CoreException { |
| try { |
| workspace.prepareOperation(); |
| ResourceInfo info = getResourceInfo(false, false); |
| int flags = getFlags(info); |
| if (!exists(flags, true) || !isOpen(flags)) |
| return; |
| |
| workspace.beginOperation(true); |
| workspace.getBuildManager().build(this, kind, builderName, args, monitor); |
| // FIXME: should we catch OperationCanceledExceptions? |
| } finally { |
| workspace.getWorkManager().avoidAutoBuild(); |
| workspace.endOperation(false, null); |
| } |
| } |
| /** |
| * @see IProject |
| */ |
| public void build(int trigger, IProgressMonitor monitor) throws CoreException { |
| try { |
| workspace.prepareOperation(); |
| ResourceInfo info = getResourceInfo(false, false); |
| int flags = getFlags(info); |
| if (!exists(flags, true) || !isOpen(flags)) |
| return; |
| |
| workspace.beginOperation(true); |
| workspace.getBuildManager().build(this, trigger, monitor); |
| // FIXME: should we catch OperationCanceledExceptions? |
| } finally { |
| workspace.getWorkManager().avoidAutoBuild(); |
| workspace.endOperation(false, null); |
| } |
| } |
| /** |
| * Checks that this resource is accessible. Typically this means that it |
| * exists. In the case of projects, they must also be open. |
| * If phantom is true, phantom resources are considered. |
| * |
| * @exception CoreException if this resource is not accessible |
| */ |
| public void checkAccessible(int flags) throws CoreException { |
| super.checkAccessible(flags); |
| if (!isOpen(flags)) { |
| String message = Policy.bind("resources.mustBeOpen", getFullPath().toString()); //$NON-NLS-1$ |
| throw new ResourceException(IResourceStatus.PROJECT_NOT_OPEN, getFullPath(), message, null); |
| } |
| } |
| /** |
| * Checks validity of the given project description. |
| */ |
| protected void checkDescription(IProject project, IProjectDescription desc, boolean moving) throws CoreException { |
| IPath location = desc.getLocation(); |
| if (location == null) |
| return; |
| String message = Policy.bind("resources.invalidProjDesc"); //$NON-NLS-1$ |
| MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INVALID_VALUE, message, null); |
| status.merge(workspace.validateName(desc.getName(), IResource.PROJECT)); |
| if (moving) { |
| // if we got here from a move call then we should check the location in the description since |
| // its possible that we want to do a rename without moving the contents. (and we shouldn't |
| // throw an Overlapping mapping exception in this case) So if the source description's location |
| // is null (we are using the default) or if the locations aren't equal, then validate the location |
| // of the new description. Otherwise both locations aren't null and they are equal so ignore validation. |
| IPath sourceLocation = internalGetDescription().getLocation(); |
| if (sourceLocation == null || !sourceLocation.equals(location)) |
| status.merge(workspace.validateProjectLocation(project, location)); |
| } else |
| // otherwise continue on like before |
| status.merge(workspace.validateProjectLocation(project, location)); |
| if (!status.isOK()) |
| throw new ResourceException(status); |
| } |
| /** |
| * @see IProject#close |
| */ |
| public void close(IProgressMonitor monitor) throws CoreException { |
| monitor = Policy.monitorFor(monitor); |
| try { |
| String msg = Policy.bind("resources.closing.1", getFullPath().toString()); //$NON-NLS-1$ |
| monitor.beginTask(msg, Policy.totalWork); |
| try { |
| workspace.prepareOperation(); |
| ResourceInfo info = getResourceInfo(false, false); |
| int flags = getFlags(info); |
| checkExists(flags, true); |
| monitor.subTask(msg); |
| if (!isOpen(flags)) |
| return; |
| // Signal that this resource is about to be closed. Do this at the very |
| // beginning so that infrastructure pieces have a chance to do clean up |
| // while the resources still exist. |
| // Do this before the begin to prevent lifecycle participants to change the tree. |
| workspace.closing(this); |
| workspace.beginOperation(true); |
| // flush the build order early in case there is a problem |
| workspace.flushBuildOrder(); |
| IProgressMonitor sub = Policy.subMonitorFor(monitor, Policy.opWork / 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); |
| IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub); |
| internalClose(); |
| monitor.worked(Policy.opWork / 2); |
| if (saveStatus != null && !saveStatus.isOK()) |
| throw new ResourceException(saveStatus); |
| } catch (OperationCanceledException e) { |
| workspace.getWorkManager().operationCanceled(); |
| throw e; |
| } finally { |
| workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /** |
| * @see IProject#copy |
| */ |
| public void copy(IProjectDescription destination, int updateFlags, IProgressMonitor monitor) throws CoreException { |
| // FIXME - the logic here for copying projects needs to be moved to Resource.copy |
| // so that IResource.copy(IProjectDescription,int,IProgressMonitor) works properly for |
| // projects and honours all update flags |
| Assert.isNotNull(destination); |
| internalCopy(destination, updateFlags, monitor); |
| } |
| /** |
| * @see IResource#copy |
| */ |
| public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException { |
| // FIXME - the logic here for copying projects needs to be moved to Resource.copy |
| // so that IResource.copy(IPath,int,IProgressMonitor) works properly for |
| // projects and honours all update flags |
| monitor = Policy.monitorFor(monitor); |
| if (destination.segmentCount() == 1) { |
| // copy project to project |
| String projectName = destination.segment(0); |
| IProjectDescription desc = getDescription(); |
| desc.setName(projectName); |
| desc.setLocation(null); |
| internalCopy(desc, updateFlags, monitor); |
| } else { |
| // will fail since we're trying to copy a project to a non-project |
| checkCopyRequirements(destination, IResource.PROJECT, updateFlags); |
| } |
| } |
| protected void copyMetaArea(IProject source, IProject destination, IProgressMonitor monitor) throws CoreException { |
| java.io.File oldMetaArea = workspace.getMetaArea().locationFor(source).toFile(); |
| java.io.File newMetaArea = workspace.getMetaArea().locationFor(destination).toFile(); |
| getLocalManager().getStore().copy(oldMetaArea, newMetaArea, IResource.DEPTH_INFINITE, monitor); |
| } |
| /** |
| * @see IProject#create |
| */ |
| public void create(IProjectDescription description, IProgressMonitor monitor) throws CoreException { |
| monitor = Policy.monitorFor(monitor); |
| try { |
| monitor.beginTask(Policy.bind("resources.create"), Policy.totalWork); //$NON-NLS-1$ |
| checkValidPath(path, PROJECT); |
| try { |
| workspace.prepareOperation(); |
| checkDoesNotExist(); |
| if (description != null) |
| checkDescription(this, description, false); |
| workspace.beginOperation(true); |
| workspace.createResource(this, false); |
| workspace.getMetaArea().create(this); |
| ProjectInfo info = (ProjectInfo) getResourceInfo(false, true); |
| |
| // setup description to obtain project location |
| ProjectDescription desc; |
| if (description == null) { |
| desc = new ProjectDescription(); |
| } else { |
| desc = (ProjectDescription)((ProjectDescription)description).clone(); |
| } |
| desc.setName(getName()); |
| info.setDescription(desc); |
| //look for a description on disk |
| try { |
| if (getLocalManager().hasSavedProject(this)) { |
| updateDescription(); |
| //make sure the .location file is written |
| workspace.getMetaArea().writeLocation(this); |
| } else { |
| //write out the project |
| writeDescription(IResource.FORCE); |
| } |
| } catch (CoreException e) { |
| workspace.deleteResource(this); |
| throw e; |
| } |
| // inaccessible projects have a null modification stamp. |
| // set this after setting the description as #setDescription |
| // updates the stamp |
| info.setModificationStamp(IResource.NULL_STAMP); |
| workspace.getSaveManager().requestSnapshot(); |
| } catch (OperationCanceledException e) { |
| workspace.getWorkManager().operationCanceled(); |
| throw e; |
| } finally { |
| workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /** |
| * @see IProject#create(IProgressMonitor) |
| */ |
| public void create(IProgressMonitor monitor) throws CoreException { |
| create(null, monitor); |
| } |
| /** |
| * @see IResource#delete(boolean, IProgressMonitor) |
| */ |
| public void delete(boolean force, IProgressMonitor monitor) throws CoreException { |
| int updateFlags = force ? IResource.FORCE : IResource.NONE; |
| delete(updateFlags, monitor); |
| } |
| protected void fixupAfterMoveSource() throws CoreException { |
| workspace.deleteResource(this); |
| } |
| |
| /** |
| * @see IProject#delete(boolean, boolean, IProgressMonitor) |
| */ |
| public void delete(boolean deleteContent, boolean force, IProgressMonitor monitor) throws CoreException { |
| int updateFlags = force ? IResource.FORCE : IResource.NONE; |
| updateFlags |= deleteContent ? IResource.ALWAYS_DELETE_PROJECT_CONTENT : IResource.NEVER_DELETE_PROJECT_CONTENT; |
| delete(updateFlags, monitor); |
| } |
| |
| /** |
| * @see IProject |
| */ |
| public IProjectDescription getDescription() throws CoreException { |
| ResourceInfo info = getResourceInfo(false, false); |
| checkAccessible(getFlags(info)); |
| return (IProjectDescription) ((ProjectInfo) info).getDescription().clone(); |
| } |
| /** |
| * @see IProject#getNature |
| */ |
| public IProjectNature getNature(String natureID) throws CoreException { |
| // Has it already been initialized? |
| ProjectInfo info = (ProjectInfo) getResourceInfo(false, false); |
| checkAccessible(getFlags(info)); |
| IProjectNature nature = info.getNature(natureID); |
| if (nature == null) { |
| // Not initialized yet. Does this project have the nature? |
| if (!hasNature(natureID)) |
| return null; |
| nature = workspace.getNatureManager().createNature(this, natureID); |
| info.setNature(natureID, nature); |
| } |
| return nature; |
| } |
| /** |
| * @see IResource#getParent |
| */ |
| public IContainer getParent() { |
| return workspace.getRoot(); |
| } |
| /** |
| * @see IProject |
| */ |
| public IPath getPluginWorkingLocation(IPluginDescriptor plugin) { |
| if (!exists()) |
| return null; |
| IPath result = workspace.getMetaArea().getWorkingLocation(this, plugin); |
| result.toFile().mkdirs(); |
| return result; |
| } |
| /** |
| * @see IResource#getProject |
| */ |
| public IProject getProject() { |
| return this; |
| } |
| /** |
| * @see IResource#getProjectRelativePath |
| */ |
| public IPath getProjectRelativePath() { |
| return Path.EMPTY; |
| } |
| /** |
| * @see IResource#getRawLocation |
| */ |
| public IPath getRawLocation() { |
| ProjectDescription description = internalGetDescription(); |
| return description == null ? null : description.getLocation(); |
| } |
| /* |
| * @see IProject |
| */ |
| public IProject[] getReferencedProjects() throws CoreException { |
| return ((ProjectDescription) internalGetDescription()).getReferencedProjects(true); |
| } |
| /** |
| * @see IProject |
| */ |
| public IProject[] getReferencingProjects() { |
| IProject[] projects = workspace.getRoot().getProjects(); |
| List result = new ArrayList(projects.length); |
| for (int i = 0; i < projects.length; i++) { |
| Project project = (Project) projects[i]; |
| if (!project.isAccessible()) |
| continue; |
| IProject[] references = project.internalGetDescription().getReferencedProjects(false); |
| for (int j = 0; j < references.length; j++) |
| if (references[j].equals(this)) { |
| result.add(projects[i]); |
| break; |
| } |
| } |
| return (IProject[]) result.toArray(new IProject[result.size()]); |
| } |
| /** |
| * @see IResource#getType |
| */ |
| public int getType() { |
| return PROJECT; |
| } |
| /** |
| * @see IProject#hasNature |
| */ |
| public boolean hasNature(String natureID) throws CoreException { |
| checkAccessible(getFlags(getResourceInfo(false, false))); |
| // use #internal method to avoid copy but still throw an |
| // exception if the resource doesn't exist. |
| IProjectDescription desc = internalGetDescription(); |
| if (desc == null) |
| checkAccessible(NULL_FLAG); |
| return desc.hasNature(natureID); |
| } |
| /** |
| * Closes the project. This is called during restore when there is a failure |
| * to read the project description. Since it is called during workspace restore, |
| * it cannot start any operations. |
| */ |
| protected void internalClose() throws CoreException { |
| workspace.flushBuildOrder(); |
| getMarkerManager().removeMarkers(this, IResource.DEPTH_INFINITE); |
| // remove each member from the resource tree. |
| // DO NOT use resource.delete() as this will delete it from disk as well. |
| IResource[] members = members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS); |
| for (int i = 0; i < members.length; i++) { |
| Resource member = (Resource) members[i]; |
| workspace.deleteResource(member); |
| } |
| // finally mark the project as closed. |
| ResourceInfo info = getResourceInfo(false, true); |
| info.clear(M_OPEN); |
| info.clearSessionProperties(); |
| info.setModificationStamp(IResource.NULL_STAMP); |
| info.setSyncInfo(null); |
| } |
| protected void internalCopy(IProjectDescription destDesc, int updateFlags, IProgressMonitor monitor) throws CoreException { |
| monitor = Policy.monitorFor(monitor); |
| try { |
| String message = Policy.bind("resources.copying", getFullPath().toString()); //$NON-NLS-1$ |
| monitor.beginTask(message, Policy.totalWork); |
| try { |
| workspace.prepareOperation(); |
| String destName = destDesc.getName(); |
| IPath destPath = new Path(destName).makeAbsolute(); |
| // The following assert method throws CoreExceptions as stated in the IProject.copy API |
| // and assert for programming errors. See checkCopyRequirements for more information. |
| assertCopyRequirements(destPath, IResource.PROJECT, updateFlags); |
| Project destProject = (Project) workspace.getRoot().getProject(destName); |
| checkDescription(destProject, destDesc, false); |
| workspace.changing(this); |
| |
| workspace.beginOperation(true); |
| getLocalManager().refresh(this, DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100)); |
| |
| // close the property store so incorrect info is not copied to the destination |
| getPropertyManager().closePropertyStore(this); |
| |
| // copy the meta area for the project |
| copyMetaArea(this, destProject, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100)); |
| |
| // copy just the project and not its children yet (tree node, properties) |
| internalCopyProjectOnly(destProject, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100)); |
| |
| // set the description |
| destProject.internalSetDescription(destDesc, false); |
| |
| // call super.copy for each child (excluding project description file) |
| IResource[] children = members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS); |
| final int childCount = children.length; |
| final int childWork = childCount > 1 ? Policy.opWork * 50 / 100 / (childCount- 1) : 0; |
| for (int i = 0; i < childCount; i++) { |
| IResource child = children[i]; |
| if (!isProjectDescriptionFile(child)) |
| child.copy(destProject.getFullPath().append(child.getName()), updateFlags, Policy.subMonitorFor(monitor, childWork)); |
| } |
| |
| // write out the new project description to the meta area |
| try { |
| destProject.writeDescription(IResource.FORCE); |
| } catch (CoreException e) { |
| try { |
| destProject.delete((updateFlags & IResource.FORCE) != 0, null); |
| } catch (CoreException e2) { |
| // ignore and rethrow the exception that got us here |
| } |
| throw e; |
| } |
| monitor.worked(Policy.opWork * 10 / 100); |
| |
| // refresh local |
| monitor.subTask(Policy.bind("resources.updating")); //$NON-NLS-1$ |
| getLocalManager().refresh(destProject, DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100)); |
| } catch (OperationCanceledException e) { |
| workspace.getWorkManager().operationCanceled(); |
| throw e; |
| } finally { |
| workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /* |
| * Copies just the project and no children. Does NOT copy the meta area. |
| */ |
| protected void internalCopyProjectOnly(IResource destination, IProgressMonitor monitor) throws CoreException { |
| // close the property store so bogus values aren't copied to the destination |
| getPropertyManager().closePropertyStore(this); |
| // copy the tree and properties |
| workspace.copyTree(this, destination.getFullPath(), IResource.DEPTH_ZERO, false, false); |
| getPropertyManager().copy(this, destination, IResource.DEPTH_ZERO); |
| |
| //clear instantiated builders and natures because they reference the project handle |
| ProjectInfo info = (ProjectInfo) ((Resource)destination).getResourceInfo(false, true); |
| info.setBuilders(null); |
| info.clearNatures(); |
| |
| //clear session properties and markers for the new project, because they shouldn't be copied. |
| info.setMarkers(null); |
| info.clearSessionProperties(); |
| } |
| |
| /** |
| * This is an internal helper method. This implementation is different from the API |
| * method getDescription(). This one does not check the project accessibility. It exists |
| * in order to prevent "chicken and egg" problems in places like the project creation. |
| * It may return null. |
| */ |
| public ProjectDescription internalGetDescription() { |
| ProjectInfo info = (ProjectInfo) getResourceInfo(false, false); |
| if (info == null) |
| return null; |
| return info.getDescription(); |
| } |
| |
| /** |
| * Sets this project's description to the given value. This is the body of the |
| * corresponding API method but is needed separately since it is used |
| * during workspace restore (i.e., when you cannot do an operation) |
| */ |
| void internalSetDescription(IProjectDescription value, boolean incrementContentId) throws CoreException { |
| ResourceInfo info = getResourceInfo(false, true); |
| ((ProjectInfo) info).setDescription((ProjectDescription) value); |
| if (incrementContentId) { |
| info.incrementContentId(); |
| //if the project is not accessible, stamp will be null and should remain null |
| if (info.getModificationStamp() != NULL_STAMP) |
| workspace.updateModificationStamp(info); |
| } |
| } |
| public void internalSetLocal(boolean flag, int depth) throws CoreException { |
| // do nothing for projects, but call for its children |
| if (depth == IResource.DEPTH_ZERO) |
| return; |
| if (depth == IResource.DEPTH_ONE) |
| depth = IResource.DEPTH_ZERO; |
| // get the children via the workspace since we know that this |
| // resource exists (it is local). |
| IResource[] children = getChildren(this, false); |
| for (int i = 0; i < children.length; i++) |
| ((Resource) children[i]).internalSetLocal(flag, depth); |
| } |
| /** |
| * @see IResource#isAccessible |
| */ |
| public boolean isAccessible() { |
| return isOpen(); |
| } |
| /** |
| * @see IResource#isLocal |
| */ |
| public boolean isLocal(int depth) { |
| // the flags parm is ignored for projects so pass anything |
| return isLocal(-1, depth); |
| } |
| |
| /** |
| * @see IResource#isLocal |
| */ |
| public boolean isLocal(int flags, int depth) { |
| // don't check the flags....projects are always local |
| if (depth == DEPTH_ZERO) |
| return true; |
| if (depth == DEPTH_ONE) |
| depth = DEPTH_ZERO; |
| // get the children via the workspace since we know that this |
| // resource exists (it is local). |
| IResource[] children = getChildren(this, false); |
| for (int i = 0; i < children.length; i++) |
| if (!children[i].isLocal(depth)) |
| return false; |
| return true; |
| } |
| /** |
| * @see IProject#isNatureEnabled(String) |
| */ |
| public boolean isNatureEnabled(String natureId) throws CoreException { |
| checkAccessible(getFlags(getResourceInfo(false, false))); |
| return workspace.getNatureManager().isNatureEnabled(this, natureId); |
| } |
| /** |
| * @see IProject |
| */ |
| public boolean isOpen() { |
| ResourceInfo info = getResourceInfo(false, false); |
| return isOpen(getFlags(info)); |
| } |
| /** |
| * @see IProject |
| */ |
| public boolean isOpen(int flags) { |
| return flags != NULL_FLAG && ResourceInfo.isSet(flags, M_OPEN); |
| } |
| /** |
| * Returns true if this resource represents the project description file, and |
| * false otherwise. |
| */ |
| protected boolean isProjectDescriptionFile(IResource resource) { |
| return resource.getType() == IResource.FILE && |
| resource.getFullPath().segmentCount() == 2 && |
| resource.getName().equals(IProjectDescription.DESCRIPTION_FILE_NAME); |
| } |
| /** |
| * @see IProject#move |
| */ |
| public void move(IProjectDescription destination, boolean force, IProgressMonitor monitor) throws CoreException { |
| Assert.isNotNull(destination); |
| move(destination, force ? IResource.FORCE : IResource.NONE, monitor); |
| } |
| /** |
| * @see IProject |
| */ |
| public void open(IProgressMonitor monitor) throws CoreException { |
| monitor = Policy.monitorFor(monitor); |
| try { |
| String msg = Policy.bind("resources.opening.1", getFullPath().toString()); //$NON-NLS-1$ |
| monitor.beginTask(msg, Policy.totalWork); |
| monitor.subTask(msg); |
| try { |
| workspace.prepareOperation(); |
| ResourceInfo info = getResourceInfo(false, false); |
| int flags = getFlags(info); |
| checkExists(flags, true); |
| if (isOpen(flags)) |
| return; |
| |
| workspace.beginOperation(true); |
| // flush the build order early in case there is a problem |
| workspace.flushBuildOrder(); |
| info = getResourceInfo(false, true); |
| info.set(M_OPEN); |
| // the M_USED flag is used to indicate the difference between opening a project |
| // for the first time and opening it from a previous close (restoring it from disk) |
| if (info.isSet(M_USED)) { |
| workspace.getSaveManager().restore(this, Policy.subMonitorFor(monitor, Policy.opWork * 30 / 100)); |
| } else { |
| info.set(M_USED); |
| workspace.updateModificationStamp(info); |
| } |
| startup(); |
| monitor.worked(Policy.opWork * 20 / 100); |
| refreshLocal(DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 50 / 100)); |
| } catch (OperationCanceledException e) { |
| workspace.getWorkManager().operationCanceled(); |
| throw e; |
| } finally { |
| workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /** |
| * The project description file has changed on disk, resulting in a changed |
| * set of linked resources. Perform the necessary creations and deletions of |
| * links to bring the links in sync with those described in the project description. |
| * @param newDescription the new project description that may have |
| * changed link descriptions. |
| * @param status ok if everything went well, otherwise an ERROR multistatus |
| * describing the problems encountered.
*/ |
| public IStatus reconcileLinks(ProjectDescription newDescription) { |
| HashMap newLinks = newDescription.getLinks(); |
| IResource[] children = null; |
| try { |
| children = members(); |
| } catch (CoreException e) { |
| return e.getStatus(); |
| } |
| String msg = Policy.bind("links.errorLinkReconcile"); //$NON-NLS-1$ |
| MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.OPERATION_FAILED, msg, null); |
| //walk over old linked resources and remove those that are no longer defined |
| for (int i = 0; i < children.length; i++) { |
| Resource oldLink = (Resource)children[i]; |
| if (!oldLink.isLinked()) |
| continue; |
| LinkDescription newLink = null; |
| if (newLinks != null) |
| newLink = (LinkDescription)newLinks.get(oldLink.getName()); |
| //if the new link is missing, or has different location or gender, then remove old link |
| if (newLink == null || !newLink.getLocation().equals(oldLink.getLocation()) || newLink.getType() != oldLink.getType()) { |
| try { |
| oldLink.delete(IResource.NONE, null); |
| } catch (CoreException e) { |
| status.merge(e.getStatus()); |
| } |
| } |
| } |
| //walk over new links and create if necessary |
| if (newLinks == null) |
| return status; |
| for (Iterator it = newLinks.values().iterator(); it.hasNext();) { |
| LinkDescription newLink = (LinkDescription) it.next(); |
| IResource existing = findMember(newLink.getName()); |
| if (existing != null) { |
| if (!existing.isLinked()) |
| //cannot create a link if a normal resource is blocking it |
| status.add(new ResourceStatus(IResourceStatus.RESOURCE_EXISTS, existing.getFullPath(), msg)); |
| } else { |
| //no conflicting old resource, just create the new link |
| try { |
| Resource toLink = newLink.getType() == IResource.FILE ? |
| (Resource)getFile(newLink.getName()) : |
| (Resource)getFolder(newLink.getName()); |
| toLink.createLink(newLink.getLocation(), IResource.ALLOW_MISSING_LOCAL, null); |
| } catch (CoreException e) { |
| status.merge(e.getStatus()); |
| } |
| } |
| } |
| return status; |
| } |
| protected void renameMetaArea(IProject source, IProject destination, IProgressMonitor monitor) throws CoreException { |
| java.io.File oldMetaArea = workspace.getMetaArea().locationFor(source).toFile(); |
| java.io.File newMetaArea = workspace.getMetaArea().locationFor(destination).toFile(); |
| getLocalManager().getStore().move(oldMetaArea, newMetaArea, false, monitor); |
| } |
| /** |
| * @see IProject |
| */ |
| public void setDescription(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException { |
| // FIXME - update flags should be honoured: |
| // KEEP_HISTORY means capture .project file in local history |
| // FORCE means overwrite any existing .project file |
| monitor = Policy.monitorFor(monitor); |
| try { |
| monitor.beginTask(Policy.bind("resources.setDesc"), Policy.totalWork); //$NON-NLS-1$ |
| try { |
| workspace.prepareOperation(); |
| ResourceInfo info = getResourceInfo(false, false); |
| checkAccessible(getFlags(info)); |
| //If we're out of sync and !FORCE, then fail. |
| //If the file is missing, we want to write the new description then throw an exception. |
| boolean hadSavedDescription = true; |
| if (((updateFlags & IResource.FORCE) == 0)) { |
| hadSavedDescription = getLocalManager().hasSavedProject(this); |
| if (hadSavedDescription && !getLocalManager().isDescriptionSynchronized(this)) { |
| String message = Policy.bind("resources.projectDescSync", getName()); //$NON-NLS-1$ |
| throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, getFullPath(), message, null); |
| } |
| } |
| //see if we have an old .prj file |
| if (!hadSavedDescription) |
| hadSavedDescription = workspace.getMetaArea().hasSavedProject(this); |
| workspace.beginOperation(true); |
| workspace.changing(this); |
| MultiStatus status = basicSetDescription((ProjectDescription) description); |
| if (hadSavedDescription && !status.isOK()) |
| throw new CoreException(status); |
| //write the new description to the .project file |
| writeDescription(internalGetDescription(), updateFlags); |
| info = getResourceInfo(false, true); |
| info.incrementContentId(); |
| workspace.updateModificationStamp(info); |
| if (!hadSavedDescription) { |
| String msg = Policy.bind("resources.missingProjectMetaRepaired", getName()); //$NON-NLS-1$ |
| status.merge(new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, getFullPath(), msg)); |
| } |
| if (!status.isOK()) |
| throw new CoreException(status); |
| } finally { |
| workspace.endOperation(true, Policy.subMonitorFor(monitor, Policy.buildWork)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /** |
| * @see IProject |
| */ |
| public void setDescription(IProjectDescription description, IProgressMonitor monitor) throws CoreException { |
| // funnel all operations to central method |
| setDescription(description, IResource.KEEP_HISTORY, monitor); |
| } |
| /** |
| * Restore the non-persisted state for the project. For example, read and set |
| * the description from the local meta area. Also, open the property store etc. |
| * This method is used when an open project is restored and so emulates |
| * the behaviour of open(). |
| */ |
| protected void startup() throws CoreException { |
| if (!isOpen()) |
| return; |
| workspace.opening(this); |
| } |
| /** |
| * @see IResource |
| */ |
| public void touch(IProgressMonitor monitor) throws CoreException { |
| monitor = Policy.monitorFor(monitor); |
| try { |
| String message = Policy.bind("resource.touch", getFullPath().toString()); //$NON-NLS-1$ |
| monitor.beginTask(message, Policy.totalWork); |
| try { |
| workspace.prepareOperation(); |
| workspace.changing(this); |
| |
| workspace.beginOperation(true); |
| super.touch(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(); |
| } |
| } |
| /** |
| * The project description file on disk is better than the description in memory. |
| * Make sure the project description in memory is synchronized with the |
| * description file contents. |
| */ |
| protected void updateDescription() throws CoreException { |
| if (isWritingDescription) |
| return; |
| workspace.changing(this); |
| ProjectDescription description = getLocalManager().read(this, false); |
| reconcileLinks(description); |
| internalSetDescription(description, true); |
| } |
| /** |
| * Writes the project description file to disk. This is the only method |
| * that should ever be writing the description, because it ensures that |
| * the description isn't then immediately discovered as an incoming |
| * change and read back from disk. |
| */ |
| public void writeDescription(int updateFlags) throws CoreException { |
| writeDescription(internalGetDescription(), updateFlags); |
| } |
| /** |
| * Writes the project description file to disk. This is the only method |
| * that should ever be writing the description, because it ensures that |
| * the description isn't then immediately discovered as an incoming |
| * change and read back from disk. |
| */ |
| public void writeDescription(IProjectDescription description, int updateFlags) throws CoreException { |
| isWritingDescription = true; |
| try { |
| getLocalManager().internalWrite(this, description, updateFlags); |
| } finally { |
| isWritingDescription = false; |
| } |
| } |
| |
| |
| } |