/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Serge Beauchamp (Freescale Semiconductor) - [229633] Project Path Variable Support
* Anton Leherbauer (Wind River) - [198591] Allow Builder to specify scheduling rule
* Francis Lynch (Wind River) - [301563] Save and load tree snapshots
* Markus Schorn (Wind River) - [306575] Save snapshot location with project
*******************************************************************************/
package org.eclipse.core.internal.resources;
import java.net.URI;
import java.util.*;
import org.eclipse.core.filesystem.*;
import org.eclipse.core.internal.events.LifecycleEvent;
import org.eclipse.core.internal.utils.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.team.IMoveDeleteHook;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.content.IContentTypeMatcher;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.osgi.util.NLS;
/**
*
* @author Stas Negara - Added sending a notification about moving the project in method move, and
* creating the project in method create.
*
*/
public class Project extends Container implements IProject {
/**
* Option constant (value 2) indicating that the snapshot location shall be persisted with the
* project for autoloading the snapshot when the project is imported in another workspace.
* <p>
* <strong>EXPERIMENTAL</strong>. This constant has been added as part of a work in progress.
* There is no guarantee that this API will work or that it will remain the same. Please do not
* use this API without consulting with the Platform Core team.
* </p>
*
* @see #saveSnapshot(int, URI, IProgressMonitor)
* @since 3.6
*/
public static final int SNAPSHOT_SET_AUTOLOAD= 2;
protected Project(IPath path, Workspace container) {
super(path, container);
}
protected void assertCreateRequirements(IProjectDescription description) throws CoreException {
checkDoesNotExist();
checkDescription(this, description, false);
URI location= description.getLocationURI();
if (location != null)
return;
//if the project is in the default location, need to check for collision with existing folder of different case
if (!Workspace.caseSensitive) {
IFileStore store= getStore();
IFileInfo localInfo= store.fetchInfo();
if (localInfo.exists()) {
String name= getLocalManager().getLocalName(store);
if (name != null && !store.getName().equals(name)) {
String msg= NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), msg, null);
}
}
}
}
/*
* 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, int updateFlags) {
String message= Messages.resources_projectDesc;
MultiStatus result= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_WRITE_METADATA, message, null);
ProjectDescription current= internalGetDescription();
current.setComment(description.getComment());
current.setSnapshotLocationURI(description.getSnapshotLocationURI());
// set the build order before setting the references or the natures
current.setBuildSpec(description.getBuildSpec(true));
// set the references before the natures
boolean flushOrder= false;
IProject[] oldReferences= current.getReferencedProjects();
IProject[] newReferences= description.getReferencedProjects();
if (!Arrays.equals(oldReferences, newReferences)) {
current.setReferencedProjects(newReferences);
flushOrder= true;
}
oldReferences= current.getDynamicReferences();
newReferences= description.getDynamicReferences();
if (!Arrays.equals(oldReferences, newReferences)) {
current.setDynamicReferences(newReferences);
flushOrder= true;
}
if (flushOrder)
workspace.flushBuildOrder();
// the natures last as this may cause recursive calls to setDescription.
if ((updateFlags & IResource.AVOID_NATURE_CONFIG) == 0)
workspace.getNatureManager().configureNatures(this, current, description, result);
else
current.setNatureIds(description.getNatureIds(false));
return result;
}
/* (non-Javadoc)
* @see IProject#build(int, IProgressMonitor)
*/
public void build(int trigger, IProgressMonitor monitor) throws CoreException {
internalBuild(trigger, null, null, monitor);
}
/* (non-Javadoc)
* @see IProject#build(int, String, Map, IProgressMonitor)
*/
public void build(int trigger, String builderName, Map args, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(builderName);
internalBuild(trigger, builderName, args, monitor);
}
/**
* 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= NLS.bind(Messages.resources_mustBeOpen, getFullPath());
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 {
URI location= desc.getLocationURI();
String message= Messages.resources_invalidProjDesc;
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.
URI sourceLocation= internalGetDescription().getLocationURI();
if (sourceLocation == null || !sourceLocation.equals(location))
status.merge(workspace.validateProjectLocationURI(project, location));
} else
// otherwise continue on like before
status.merge(workspace.validateProjectLocationURI(project, location));
if (!status.isOK())
throw new ResourceException(status);
}
/* (non-Javadoc)
* @see IProject#close(IProgressMonitor)
*/
public void close(IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
String msg= NLS.bind(Messages.resources_closing_1, getName());
monitor.beginTask(msg, Policy.totalWork);
final ISchedulingRule rule= workspace.getRuleFactory().modifyRule(this);
try {
workspace.prepareOperation(rule, monitor);
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.
workspace.beginOperation(true);
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, this));
// 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(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IResource#copy(IPath, int, IProgressMonitor)
*/
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);
((ProjectDescription)desc).setSnapshotLocationURI(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);
}
}
/* (non-Javadoc)
* @see IResource#copy(IProjectDescription, int, IProgressMonitor)
*/
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);
}
protected void copyMetaArea(IProject source, IProject destination, IProgressMonitor monitor) throws CoreException {
IFileStore oldMetaArea= EFS.getFileSystem(EFS.SCHEME_FILE).getStore(workspace.getMetaArea().locationFor(source));
IFileStore newMetaArea= EFS.getFileSystem(EFS.SCHEME_FILE).getStore(workspace.getMetaArea().locationFor(destination));
oldMetaArea.copy(newMetaArea, EFS.NONE, monitor);
}
/* (non-Javadoc)
* @see IProject#create(IProgressMonitor)
*/
public void create(IProgressMonitor monitor) throws CoreException {
create(null, monitor);
}
/* (non-Javadoc)
* @see IProject#create(IProjectDescription, IProgressMonitor)
*/
public void create(IProjectDescription description, IProgressMonitor monitor) throws CoreException {
create(description, IResource.NONE, monitor);
}
/* (non-Javadoc)
* @see IProject#create(IProjectDescription, IProgressMonitor)
*/
public void create(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
monitor.beginTask(Messages.resources_create, Policy.totalWork);
checkValidPath(path, PROJECT, false);
final ISchedulingRule rule= workspace.getRuleFactory().createRule(this);
//CODINGSPECTATOR - added variable 'success' and all code accessing it.
boolean success= false;
try {
workspace.prepareOperation(rule, monitor);
if (description == null) {
description= new ProjectDescription();
description.setName(getName());
}
assertCreateRequirements(description);
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CREATE, this));
workspace.beginOperation(true);
workspace.createResource(this, updateFlags);
workspace.getMetaArea().create(this);
ProjectInfo info= (ProjectInfo)getResourceInfo(false, true);
// setup description to obtain project location
ProjectDescription desc= (ProjectDescription)((ProjectDescription)description).clone();
desc.setLocationURI(FileUtil.canonicalURI(description.getLocationURI()));
desc.setName(getName());
internalSetDescription(desc, false);
// see if there potentially are already contents on disk
final boolean hasSavedDescription= getLocalManager().hasSavedDescription(this);
boolean hasContent= hasSavedDescription;
//if there is no project description, there might still be content on disk
if (!hasSavedDescription)
hasContent= getLocalManager().hasSavedContent(this);
try {
// look for a description on disk
if (hasSavedDescription) {
updateDescription();
//make sure the .location file is written
workspace.getMetaArea().writePrivateDescription(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.clearModificationStamp();
//if a project already had content on disk, mark the project as having unknown children
if (hasContent)
info.set(ICoreConstants.M_CHILDREN_UNKNOWN);
workspace.getSaveManager().requestSnapshot();
success= true;
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
resourceListener.createdResource(this, updateFlags, success);
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @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);
}
/* (non-Javadoc)
* @see IResource#delete(boolean, IProgressMonitor)
*/
public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags= force ? IResource.FORCE : IResource.NONE;
delete(updateFlags, monitor);
}
public void deleteResource(boolean convertToPhantom, MultiStatus status) throws CoreException {
super.deleteResource(convertToPhantom, status);
// Delete the project metadata.
workspace.getMetaArea().delete(this);
// Clear the history store.
clearHistory(null);
}
protected void fixupAfterMoveSource() throws CoreException {
workspace.deleteResource(this);
// check if we deleted a preferences file
ProjectPreferences.deleted(this);
}
/*
* (non-Javadoc)
* @see IProject#getContentTypeMatcher
*/
public IContentTypeMatcher getContentTypeMatcher() throws CoreException {
return workspace.getContentDescriptionManager().getContentTypeMatcher(this);
}
/* (non-Javadoc)
* @see IContainer#getDefaultCharset(boolean)
*/
public String getDefaultCharset(boolean checkImplicit) {
// non-existing resources default to parent's charset
if (!exists())
return checkImplicit ? ResourcesPlugin.getEncoding() : null;
return workspace.getCharsetManager().getCharsetFor(getFullPath(), checkImplicit);
}
/* (non-Javadoc)
* @see IProject#getDescription()
*/
public IProjectDescription getDescription() throws CoreException {
ResourceInfo info= getResourceInfo(false, false);
checkAccessible(getFlags(info));
ProjectDescription description= ((ProjectInfo)info).getDescription();
//if the project is currently in the middle of being created, the description might not be available yet
if (description == null)
checkAccessible(NULL_FLAG);
return (IProjectDescription)description.clone();
}
/* (non-Javadoc)
* @see IProject#getNature(String)
*/
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;
}
/* (non-Javadoc)
* @see IResource#getParent()
*/
public IContainer getParent() {
return workspace.getRoot();
}
/**
* (non-Javadoc)
*
* @see IProject#getPluginWorkingLocation(IPluginDescriptor)
* @deprecated
*/
public IPath getPluginWorkingLocation(IPluginDescriptor plugin) {
if (plugin == null)
return null;
return getWorkingLocation(plugin.getUniqueIdentifier());
}
/* (non-Javadoc)
* @see IResource#getProject()
*/
public IProject getProject() {
return this;
}
/* (non-Javadoc)
* @see IResource#getProjectRelativePath()
*/
public IPath getProjectRelativePath() {
return Path.EMPTY;
}
/* (non-Javadoc)
* @see IResource#getRawLocation()
*/
public IPath getRawLocation() {
ProjectDescription description= internalGetDescription();
return description == null ? null : description.getLocation();
}
/* (non-Javadoc)
* @see IResource#getRawLocation()
*/
public URI getRawLocationURI() {
ProjectDescription description= internalGetDescription();
return description == null ? null : description.getLocationURI();
}
/* (non-Javadoc)
* @see IProject#getReferencedProjects()
*/
public IProject[] getReferencedProjects() throws CoreException {
ResourceInfo info= getResourceInfo(false, false);
checkAccessible(getFlags(info));
ProjectDescription description= ((ProjectInfo)info).getDescription();
//if the project is currently in the middle of being created, the description might not be available yet
if (description == null)
checkAccessible(NULL_FLAG);
return description.getAllReferences(true);
}
/* (non-Javadoc)
* @see IProject#getReferencingProjects()
*/
public IProject[] getReferencingProjects() {
IProject[] projects= workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
List result= new ArrayList(projects.length);
for (int i= 0; i < projects.length; i++) {
Project project= (Project)projects[i];
if (!project.isAccessible())
continue;
ProjectDescription description= project.internalGetDescription();
if (description == null)
continue;
IProject[] references= description.getAllReferences(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()]);
}
/* (non-Javadoc)
* @see IResource#getType()
*/
public int getType() {
return PROJECT;
}
/*
* (non-Javadoc)
* @see IProject#getWorkingLocation(String)
*/
public IPath getWorkingLocation(String id) {
if (id == null || !exists())
return null;
IPath result= workspace.getMetaArea().getWorkingLocation(this, id);
result.toFile().mkdirs();
return result;
}
/* (non-Javadoc)
* @see IProject#hasNature(String)
*/
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);
}
/**
* Implements all build methods on IProject.
*/
protected void internalBuild(final int trigger, final String builderName, final Map args, IProgressMonitor monitor) throws CoreException {
workspace.run(new IWorkspaceRunnable() {
public void run(IProgressMonitor innerMonitor) throws CoreException {
innerMonitor= Policy.monitorFor(innerMonitor);
final ISchedulingRule rule= workspace.getRoot();
try {
innerMonitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
try {
workspace.prepareOperation(rule, innerMonitor);
if (!shouldBuild())
return;
workspace.beginOperation(true);
workspace.aboutToBuild(Project.this, trigger);
} finally {
workspace.endOperation(rule, false, innerMonitor);
}
final ISchedulingRule buildRule= workspace.getBuildManager().getRule(Project.this, trigger, builderName, args);
try {
IStatus result;
workspace.prepareOperation(buildRule, innerMonitor);
//don't open the tree eagerly because it will be wasted if no build occurs
workspace.beginOperation(false);
result= workspace.getBuildManager().build(Project.this, trigger, builderName, args, Policy.subMonitorFor(innerMonitor, Policy.opWork));
if (!result.isOK())
throw new ResourceException(result);
} finally {
workspace.endOperation(buildRule, false, innerMonitor);
try {
workspace.prepareOperation(rule, innerMonitor);
//don't open the tree eagerly because it will be wasted if no change occurs
workspace.beginOperation(false);
workspace.broadcastBuildEvent(Project.this, IResourceChangeEvent.POST_BUILD, trigger);
//building may close the tree, so open it
if (workspace.getElementTree().isImmutable())
workspace.newWorkingTree();
} finally {
workspace.endOperation(rule, false, Policy.subMonitorFor(innerMonitor, Policy.endOpWork));
}
}
} finally {
innerMonitor.done();
}
}
/**
* Returns whether this project should be built for a given trigger.
*
* @return <code>true</code> if the build should proceed, and <code>false</code>
* otherwise.
*/
private boolean shouldBuild() {
ResourceInfo info= getResourceInfo(false, false);
int flags= getFlags(info);
if (!exists(flags, true) || !isOpen(flags))
return false;
return true;
}
}, null, IWorkspace.AVOID_UPDATE, monitor);
}
/**
* 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 | IContainer.INCLUDE_HIDDEN);
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.clearModificationStamp();
info.setSyncInfo(null);
}
//CODINGSPECTATOR - Note: No need to record this event directly, because copying of children will be recorded in Resource.copy(IPath...).
protected void internalCopy(IProjectDescription destDesc, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
String message= NLS.bind(Messages.resources_copying, getFullPath());
monitor.beginTask(message, Policy.totalWork);
String destName= destDesc.getName();
IPath destPath= new Path(destName).makeAbsolute();
Project destination= (Project)workspace.getRoot().getProject(destName);
final ISchedulingRule rule= workspace.getRuleFactory().copyRule(this, destination);
try {
workspace.prepareOperation(rule, monitor);
// 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);
checkDescription(destination, destDesc, false);
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_COPY, this, destination, updateFlags));
workspace.beginOperation(true);
getLocalManager().refresh(this, DEPTH_INFINITE, true, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
// close the property store so incorrect info is not copied to the destination
getPropertyManager().closePropertyStore(this);
getLocalManager().getHistoryStore().closeHistoryStore(this);
// copy the meta area for the project
copyMetaArea(this, destination, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
// copy just the project and not its children yet (tree node, properties)
internalCopyProjectOnly(destination, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
// set the description
destination.internalSetDescription(destDesc, false);
//create the directory for the new project
destination.getStore().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
// call super.copy for each child (excluding project description file)
//make it a best effort copy
message= Messages.resources_copyProblem;
MultiStatus problems= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
IResource[] children= members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
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)) {
try {
child.copy(destPath.append(child.getName()), updateFlags, Policy.subMonitorFor(monitor, childWork));
} catch (CoreException e) {
problems.merge(e.getStatus());
}
}
}
// write out the new project description to the meta area
try {
destination.writeDescription(IResource.FORCE);
} catch (CoreException e) {
try {
destination.delete((updateFlags & IResource.FORCE) != 0, null);
} catch (CoreException e2) {
// ignore and rethrow the exception that got us here
}
throw e;
}
monitor.worked(Policy.opWork * 5 / 100);
// refresh local
monitor.subTask(Messages.resources_updating);
getLocalManager().refresh(destination, DEPTH_INFINITE, true, Policy.subMonitorFor(monitor, Policy.opWork * 10 / 100));
if (!problems.isOK())
throw new ResourceException(problems);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} 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);
getLocalManager().getHistoryStore().closeHistoryStore(this);
// copy the tree and properties
workspace.copyTree(this, destination.getFullPath(), IResource.DEPTH_ZERO, IResource.NONE, false);
getPropertyManager().copy(this, destination, IResource.DEPTH_ZERO);
ProjectInfo info= (ProjectInfo)((Resource)destination).getResourceInfo(false, true);
//clear properties, markers, and description for the new project, because they shouldn't be copied.
info.description= null;
info.natures= null;
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) {
ProjectInfo info= (ProjectInfo)getResourceInfo(false, true);
info.setDescription((ProjectDescription)value);
getLocalManager().setLocation(this, info, value.getLocationURI());
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(IResource.NONE);
for (int i= 0; i < children.length; i++)
((Resource)children[i]).internalSetLocal(flag, depth);
}
/* (non-Javadoc)
* @see IResource#isAccessible()
*/
public boolean isAccessible() {
return isOpen();
}
/* (non-Javadoc)
* @see org.eclipse.core.internal.resources.Resource#isDerived(int)
*/
public boolean isDerived(int options) {
//projects are never derived
return false;
}
/* (non-Javadoc)
* @see IResource#isLinked(int)
*/
public boolean isLinked(int options) {
return false;//projects are never linked
}
public boolean isVirtual() {
return false; // projects are never virtual
}
/* (non-Javadoc)
* @see IResource#isTeamPrivateMember(int)
*/
public boolean isTeamPrivateMember(int options) {
return false;//projects are never team private members
}
/**
* @see IResource#isLocal(int)
* @deprecated
*/
public boolean isLocal(int depth) {
// the flags parameter is ignored for projects so pass anything
return isLocal(-1, depth);
}
/**
* @see IResource#isLocal(int)
* @deprecated
*/
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(IResource.NONE);
for (int i= 0; i < children.length; i++)
if (!children[i].isLocal(depth))
return false;
return true;
}
/* (non-Javadoc)
* @see IProject#isNatureEnabled(String)
*/
public boolean isNatureEnabled(String natureId) throws CoreException {
checkAccessible(getFlags(getResourceInfo(false, false)));
return workspace.getNatureManager().isNatureEnabled(this, natureId);
}
/* (non-Javadoc)
* @see IProject#isOpen()
*/
public boolean isOpen() {
ResourceInfo info= getResourceInfo(false, false);
return isOpen(getFlags(info));
}
/* (non-Javadoc)
* @see IProject#isOpen()
*/
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);
}
/* (non-Javadoc)
* @see IProject#loadSnapshot(int, URI, IProgressMonitor)
*/
public void loadSnapshot(int options, URI snapshotLocation, IProgressMonitor monitor) throws CoreException {
// load a snapshot of refresh information when project is not opened
if (isOpen()) {
String message= Messages.resources_projectMustNotBeOpen;
MultiStatus status= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
throw new CoreException(status);
}
internalLoadSnapshot(options, snapshotLocation, monitor);
}
/**
* Loads a snapshot of project meta-data from the given location URI. Like
* {@link IProject#loadSnapshot(int, URI, IProgressMonitor)} but can be called when the project
* is open.
*
* @see IProject#saveSnapshot(int, URI, IProgressMonitor)
*/
private void internalLoadSnapshot(int options, URI snapshotLocation, IProgressMonitor monitor) throws CoreException {
if ((options & SNAPSHOT_TREE) != 0) {
// ensure that path variables are resolved: only ws accessible while project is closed
snapshotLocation= workspace.getPathVariableManager().resolveURI(snapshotLocation);
if (!snapshotLocation.isAbsolute()) {
String message= NLS.bind(Messages.projRead_badSnapshotLocation, snapshotLocation.toString());
throw new CoreException(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, message, null));
}
// copy the snapshot from the URI into the project metadata
IPath snapshotPath= workspace.getMetaArea().getRefreshLocationFor(this);
IFileStore snapshotFileStore= EFS.getStore(org.eclipse.core.filesystem.URIUtil.toURI(snapshotPath));
EFS.getStore(snapshotLocation).copy(snapshotFileStore, EFS.OVERWRITE, monitor);
}
}
/* (non-Javadoc)
* @see IProject#move(IProjectDescription, boolean, IProgressMonitor)
*/
public void move(IProjectDescription destination, boolean force, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(destination);
move(destination, force ? IResource.FORCE : IResource.NONE, monitor);
}
/* (non-Javadoc)
* @see IResource#move(IProjectDescription, int, IProgressMonitor)
*/
public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(description);
monitor= Policy.monitorFor(monitor);
try {
String message= NLS.bind(Messages.resources_moving, getFullPath());
monitor.beginTask(message, Policy.totalWork);
IProject destination= workspace.getRoot().getProject(description.getName());
final ISchedulingRule rule= workspace.getRuleFactory().moveRule(this, destination);
//CODINGSPECTATOR - added variable 'success' and all code accessing it.
boolean success= false;
try {
workspace.prepareOperation(rule, monitor);
// The following assert method throws CoreExceptions as stated in the IResource.move API
// and assert for programming errors. See checkMoveRequirements for more information.
if (!getName().equals(description.getName())) {
IPath destPath= Path.ROOT.append(description.getName());
assertMoveRequirements(destPath, IResource.PROJECT, updateFlags);
}
checkDescription(destination, description, true);
workspace.beginOperation(true);
message= Messages.resources_moveProblem;
MultiStatus status= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null);
WorkManager workManager= workspace.getWorkManager();
ResourceTree tree= new ResourceTree(getLocalManager(), workManager.getLock(), status, updateFlags);
IMoveDeleteHook hook= workspace.getMoveDeleteHook();
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_MOVE, this, destination, updateFlags));
int depth= 0;
try {
depth= workManager.beginUnprotected();
if (!hook.moveProject(tree, this, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2)))
tree.standardMoveProject(this, description, updateFlags, Policy.subMonitorFor(monitor, Policy.opWork / 2));
} finally {
workManager.endUnprotected(depth);
}
// Invalidate the tree for further use by clients.
tree.makeInvalid();
if (!tree.getStatus().isOK())
throw new ResourceException(tree.getStatus());
success= true;
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
resourceListener.movedResource(this, destination.getFullPath(), updateFlags, success);
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IProject#open(IProgressMonitor)
*/
public void open(int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
String msg= NLS.bind(Messages.resources_opening_1, getName());
monitor.beginTask(msg, Policy.totalWork);
monitor.subTask(msg);
final ISchedulingRule rule= workspace.getRuleFactory().modifyRule(this);
try {
workspace.prepareOperation(rule, monitor);
ProjectInfo info= (ProjectInfo)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= (ProjectInfo)getResourceInfo(false, true);
info.set(M_OPEN);
//clear the unknown children immediately to avoid background refresh
boolean unknownChildren= info.isSet(M_CHILDREN_UNKNOWN);
if (unknownChildren)
info.clear(M_CHILDREN_UNKNOWN);
// 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)
boolean used= info.isSet(M_USED);
boolean snapshotLoaded= false;
if (!used && !workspace.getMetaArea().getRefreshLocationFor(this).toFile().exists()) {
//auto-load a refresh snapshot if it is set
final boolean hasSavedDescription= getLocalManager().hasSavedDescription(this);
if (hasSavedDescription) {
ProjectDescription updatedDesc= info.getDescription();
if (updatedDesc != null) {
URI autoloadURI= updatedDesc.getSnapshotLocationURI();
if (autoloadURI != null) {
try {
autoloadURI= getPathVariableManager().resolveURI(autoloadURI);
internalLoadSnapshot(SNAPSHOT_TREE, autoloadURI, Policy.subMonitorFor(monitor, Policy.opWork * 5 / 100));
snapshotLoaded= true;
} catch (CoreException ce) {
//Log non-existing autoload snapshot as warning only
String msgerr= NLS.bind(Messages.projRead_cannotReadSnapshot, getName(), ce.getLocalizedMessage());
Policy.log(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, msgerr));
}
}
}
}
}
boolean minorIssuesDuringRestore= false;
if (used) {
minorIssuesDuringRestore= workspace.getSaveManager().restore(this, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
} else {
info.set(M_USED);
//reconcile any links and groups in the project description
IStatus result= reconcileLinksAndGroups(info.getDescription());
if (!result.isOK())
throw new CoreException(result);
workspace.updateModificationStamp(info);
monitor.worked(Policy.opWork * (snapshotLoaded ? 15 : 20) / 100);
}
startup();
//request a refresh if the project is new and has unknown members on disk
// or restore of the project is not fully successful
if ((!used && unknownChildren) || !minorIssuesDuringRestore) {
boolean refreshed= false;
if (!used) {
refreshed= workspace.getSaveManager().restoreFromRefreshSnapshot(
this, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100));
if (refreshed) { // account for the refresh work
monitor.worked(Policy.opWork * 60 / 100);
}
}
//refresh either in background or foreground
if (!refreshed) {
if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) {
workspace.refreshManager.refresh(this);
monitor.worked(Policy.opWork * 60 / 100);
} else {
refreshLocal(IResource.DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100));
}
}
}
//creation of this project may affect overlapping resources
workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_INFINITE, monitor);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IProject#open(IProgressMonitor)
*/
public void open(IProgressMonitor monitor) throws CoreException {
open(IResource.NONE, monitor);
}
/**
* 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.
* @return status ok if everything went well, otherwise an ERROR multi-status describing the
* problems encountered.
*/
public IStatus reconcileLinksAndGroups(ProjectDescription newDescription) {
String msg= Messages.links_errorLinkReconcile;
HashMap newLinks= newDescription.getLinks();
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
ProjectDescription oldDescription= internalGetDescription();
if (oldDescription != null) {
HashMap oldLinks= oldDescription.getLinks();
if (oldLinks != null) {
for (Iterator it= oldLinks.values().iterator(); it.hasNext();) {
LinkDescription oldLink= (LinkDescription)it.next();
Resource oldLinkResource= (Resource)findMember(oldLink.getProjectRelativePath());
if (oldLinkResource == null || !oldLinkResource.isLinked())
continue;
LinkDescription newLink= null;
if (newLinks != null)
newLink= (LinkDescription)newLinks.get(oldLink.getProjectRelativePath());
//if the new link is missing, or has different (raw) location or gender, then remove old link
if (newLink == null || !newLink.getLocationURI().equals(oldLinkResource.getRawLocationURI()) || newLink.getType() != oldLinkResource.getType()) {
try {
oldLinkResource.delete(IResource.NONE, null);
//refresh the resource, because removing a link can reveal a previously hidden resource in parent
oldLinkResource.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
status.merge(e.getStatus());
}
}
}
}
}
// walk over new links and groups and create if necessary
// Recreate them in order of the higher up in the tree hierarchy first,
// so we don't have to create intermediate directories that would turn
// out
// to be groups or link folders.
if (newLinks == null)
return status;
//sort links to avoid creating nested links before their parents
TreeSet newLinksAndGroups= new TreeSet(new Comparator() {
public int compare(Object arg0, Object arg1) {
int numberOfSegments0= ((LinkDescription)arg0).getProjectRelativePath().segmentCount();
int numberOfSegments1= ((LinkDescription)arg1).getProjectRelativePath().segmentCount();
if (numberOfSegments0 != numberOfSegments1)
return numberOfSegments0 - numberOfSegments1;
else if (arg0.equals(arg1))
return 0;
return -1;
}
});
if (newLinks != null)
newLinksAndGroups.addAll(newLinks.values());
for (Iterator it= newLinksAndGroups.iterator(); it.hasNext();) {
Object description= it.next();
LinkDescription newLink= (LinkDescription)description;
try {
Resource toLink= workspace.newResource(getFullPath().append(newLink.getProjectRelativePath()), newLink.getType());
IContainer parent= toLink.getParent();
if (parent != null && !parent.exists() && parent.getType() == FOLDER)
((Folder)parent).ensureExists(Policy.monitorFor(null));
if (!toLink.exists() || !toLink.isLinked()) {
if (newLink.isGroup())
((Folder)toLink).create(IResource.REPLACE | IResource.VIRTUAL, true, null);
else
toLink.createLink(newLink.getLocationURI(), IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null);
}
} catch (CoreException e) {
status.merge(e.getStatus());
}
}
return status;
}
/* (non-Javadoc)
* @see IProject#saveSnapshot(int, URI, IProgressMonitor)
*/
public void saveSnapshot(int options, URI snapshotLocation, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
//Project must be open such that variables can be resolved
checkAccessible(getFlags(getResourceInfo(false, false)));
//URI must not be null and must not refer to undefined path variables
URI resolvedSnapshotLocation= getPathVariableManager().resolveURI(snapshotLocation);
if (resolvedSnapshotLocation == null || !resolvedSnapshotLocation.isAbsolute()) {
String message= NLS.bind(Messages.projRead_badSnapshotLocation, resolvedSnapshotLocation);
throw new CoreException(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, message, null));
}
if ((options & SNAPSHOT_TREE) != 0) {
// write a snapshot of refresh information
try {
IProgressMonitor sub= Policy.subMonitorFor(monitor, Policy.opWork / 2);
workspace.getSaveManager().saveRefreshSnapshot(this, resolvedSnapshotLocation, sub);
} catch (OperationCanceledException e) {
//workspace.getWorkManager().operationCanceled();
throw e;
}
}
if ((options & SNAPSHOT_SET_AUTOLOAD) != 0) {
IProgressMonitor sub= Policy.subMonitorFor(monitor, Policy.opWork / 2);
//Make absolute URI inside the project relative
if (snapshotLocation != null && snapshotLocation.isAbsolute()) {
snapshotLocation= getPathVariableManager().convertToRelative(snapshotLocation, false, "PROJECT_LOC"); //$NON-NLS-1$
}
IProjectDescription desc= getDescription();
((ProjectDescription)desc).setSnapshotLocationURI(snapshotLocation);
setDescription(desc, IResource.KEEP_HISTORY | IResource.AVOID_NATURE_CONFIG, sub);
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
*/
public void setDescription(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
// FIXME - update flags should be honored:
// KEEP_HISTORY means capture .project file in local history
// FORCE means overwrite any existing .project file
monitor= Policy.monitorFor(monitor);
try {
monitor.beginTask(Messages.resources_setDesc, Policy.totalWork);
ISchedulingRule rule= null;
if ((updateFlags & IResource.AVOID_NATURE_CONFIG) != 0)
rule= workspace.getRuleFactory().modifyRule(this);
else
rule= workspace.getRoot();
try {
//need to use root rule because nature configuration calls third party code
workspace.prepareOperation(rule, monitor);
ResourceInfo info= getResourceInfo(false, false);
checkAccessible(getFlags(info));
//if nothing has changed, we don't need to do anything
ProjectDescription oldDescription= internalGetDescription();
ProjectDescription newDescription= (ProjectDescription)description;
boolean hasPublicChanges= oldDescription.hasPublicChanges(newDescription);
boolean hasPrivateChanges= oldDescription.hasPrivateChanges(newDescription);
if (!hasPublicChanges && !hasPrivateChanges)
return;
checkDescription(this, newDescription, false);
//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().hasSavedDescription(this);
if (hadSavedDescription && !getLocalManager().isDescriptionSynchronized(this)) {
String message= NLS.bind(Messages.resources_projectDescSync, getName());
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.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
workspace.beginOperation(true);
MultiStatus status= basicSetDescription(newDescription, updateFlags);
if (hadSavedDescription && !status.isOK())
throw new CoreException(status);
//write the new description to the .project file
writeDescription(oldDescription, updateFlags, hasPublicChanges, hasPrivateChanges);
//increment the content id even for private changes
info= getResourceInfo(false, true);
info.incrementContentId();
workspace.updateModificationStamp(info);
if (!hadSavedDescription) {
String msg= NLS.bind(Messages.resources_missingProjectMetaRepaired, getName());
status.merge(new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, getFullPath(), msg));
}
if (!status.isOK())
throw new CoreException(status);
} finally {
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IProject#setDescription(IProjectDescription, IProgressMonitor)
*/
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.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_OPEN, this));
}
/* (non-Javadoc)
* @see IResource#touch(IProgressMonitor)
*/
public void touch(IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
String message= NLS.bind(Messages.resources_touch, getFullPath());
monitor.beginTask(message, Policy.totalWork);
final ISchedulingRule rule= workspace.getRuleFactory().modifyRule(this);
try {
workspace.prepareOperation(rule, monitor);
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
workspace.beginOperation(true);
super.touch(Policy.subMonitorFor(monitor, Policy.opWork));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} 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 (ProjectDescription.isWriting)
return;
ProjectDescription.isReading= true;
try {
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CHANGE, this));
ProjectDescription description= getLocalManager().read(this, false);
//links can only be created if the project is open
IStatus result= null;
if (isOpen())
result= reconcileLinksAndGroups(description);
internalSetDescription(description, true);
if (result != null && !result.isOK())
throw new CoreException(result);
} finally {
ProjectDescription.isReading= false;
}
}
/**
* Writes the project's current description file to disk.
*/
public void writeDescription(int updateFlags) throws CoreException {
writeDescription(internalGetDescription(), updateFlags, true, 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.
*
* @param description The description to write
* @param updateFlags The write operation update flags
* @param hasPublicChanges Whether the public sections of the description have changed
* @param hasPrivateChanges Whether the private sections of the description have changed
* @exception CoreException On failure to write the description
*/
public void writeDescription(IProjectDescription description, int updateFlags, boolean hasPublicChanges, boolean hasPrivateChanges) throws CoreException {
if (ProjectDescription.isReading)
return;
ProjectDescription.isWriting= true;
try {
getLocalManager().internalWrite(this, description, updateFlags, hasPublicChanges, hasPrivateChanges);
} finally {
ProjectDescription.isWriting= false;
}
}
}