org.eclipse.core.internal.resources.Project Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during
compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based
@AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step.
This library is a superset of AspectJ weaver and hence also of AspectJ runtime.
/*******************************************************************************
* Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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
* Broadcom Corporation - ongoing development
* Lars Vogel - Bug 473427
* Christoph Läubrich - Issue #80 - CharsetManager access the ResourcesPlugin.getWorkspace before init
*******************************************************************************/
package org.eclipse.core.internal.resources;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.internal.events.LifecycleEvent;
import org.eclipse.core.internal.preferences.EclipsePreferences;
import org.eclipse.core.internal.utils.FileUtil;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.team.IMoveDeleteHook;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ICoreRunnable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.content.IContentTypeMatcher;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.osgi.util.NLS;
import org.osgi.service.prefs.Preferences;
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.
*
* EXPERIMENTAL. 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.
*
* @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, IPath.fromOSString(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;
}
// Update the dynamic state
flushOrder |= current.updateDynamicState(description);
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;
}
@Override
public void build(int trigger, IProgressMonitor monitor) throws CoreException {
if (!isAccessible())
return;
internalBuild(getActiveBuildConfig(), trigger, null, null, monitor);
}
@Override
public void build(int trigger, String builderName, Map args, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(builderName);
if (!isAccessible())
return;
internalBuild(getActiveBuildConfig(), trigger, builderName, args, monitor);
}
@Override
public void build(IBuildConfiguration config, int trigger, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(config);
// If project isn't accessible, or doesn't contain the build configuration, nothing to do.
if (!isAccessible() || !hasBuildConfig(config.getName()))
return;
internalBuild(config, trigger, null, null, 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
*/
@Override
public void checkAccessible(int flags) throws CoreException {
super.checkAccessible(flags);
if (!isOpen(flags)) {
String message = NLS.bind(Messages.resources_mustBeOpen, getName());
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);
}
@Override
public void close(IProgressMonitor monitor) throws CoreException {
String msg = NLS.bind(Messages.resources_closing_1, getName());
SubMonitor subMonitor = SubMonitor.convert(monitor, msg, 100);
final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
SubMonitor newChild = subMonitor.newChild(1);
try {
workspace.prepareOperation(rule, newChild);
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkExists(flags, true);
subMonitor.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 = subMonitor.newChild(49, SubMonitor.SUPPRESS_SUBTASK);
IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub);
internalClose(subMonitor.newChild(49));
if (saveStatus != null && !saveStatus.isOK())
throw new ResourceException(saveStatus);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
subMonitor.done();
workspace.endOperation(rule, true);
}
}
@Override
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);
}
}
@Override
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);
}
@Override
public void create(IProgressMonitor monitor) throws CoreException {
create(null, monitor);
}
@Override
public void create(IProjectDescription description, IProgressMonitor monitor) throws CoreException {
create(description, IResource.NONE, monitor);
}
@Override
public void create(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.resources_create, 100);
checkValidPath(path, PROJECT, false);
final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
try {
workspace.prepareOperation(rule, subMonitor);
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();
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
subMonitor.done();
workspace.endOperation(rule, true);
}
}
@Override
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);
}
@Override
public void delete(boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags = force ? IResource.FORCE : IResource.NONE;
delete(updateFlags, monitor);
}
@Override
public void deleteResource(boolean convertToPhantom, MultiStatus status) throws CoreException {
super.deleteResource(convertToPhantom, status);
// Clear the history store.
clearHistory(null);
// Delete the project metadata.
workspace.getMetaArea().delete(this);
}
@Override
protected void fixupAfterMoveSource() throws CoreException {
workspace.deleteResource(this);
// check if we deleted a preferences file
ProjectPreferences.deleted(this);
}
@Override
public IBuildConfiguration getActiveBuildConfig() throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
return internalGetActiveBuildConfig();
}
@Override
public IBuildConfiguration getBuildConfig(String configName) throws CoreException {
if (configName == null)
return getActiveBuildConfig();
ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
checkAccessible(getFlags(info));
IBuildConfiguration[] configs = internalGetBuildConfigs(false);
for (IBuildConfiguration config : configs) {
if (config.getName().equals(configName)) {
return config;
}
}
throw new ResourceException(IResourceStatus.BUILD_CONFIGURATION_NOT_FOUND, getFullPath(), null, null);
}
@Override
public IBuildConfiguration[] getBuildConfigs() throws CoreException {
ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
checkAccessible(getFlags(info));
return internalGetBuildConfigs(true);
}
@Override
public IContentTypeMatcher getContentTypeMatcher() throws CoreException {
return workspace.getContentDescriptionManager().getContentTypeMatcher(this);
}
@Override
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);
}
@Override
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();
}
@Override
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;
}
@Override
public IContainer getParent() {
return workspace.getRoot();
}
@Override
public IProject getProject() {
return this;
}
@Override
public IPath getProjectRelativePath() {
return IPath.EMPTY;
}
@Override
public IPath getRawLocation() {
ProjectDescription description = internalGetDescription();
return description == null ? null : description.getLocation();
}
@Override
public URI getRawLocationURI() {
ProjectDescription description = internalGetDescription();
return description == null ? null : description.getLocationURI();
}
@Override
public IBuildConfiguration[] getReferencedBuildConfigs(String configName, boolean includeMissing) 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);
if (!hasBuildConfig(configName))
throw new ResourceException(IResourceStatus.BUILD_CONFIGURATION_NOT_FOUND, getFullPath(), null, null);
return internalGetReferencedBuildConfigs(configName, includeMissing);
}
@Override
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(this, true);
}
@Override
public void clearCachedDynamicReferences() {
ResourceInfo info = getResourceInfo(false, false);
if (info == null) {
// If the project is not open there is no cached state and so there is nothing to do.
return;
}
ProjectDescription description = ((ProjectInfo) info).getDescription();
if (description == null) {
// If the project is in the process of being created there is no cached state and nothing to do.
return;
}
description.clearCachedDynamicReferences(null);
}
@Override
public IProject[] getReferencingProjects() {
IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
List result = new ArrayList<>(projects.length);
for (IProject p : projects) {
Project project = (Project) p;
if (!project.isAccessible())
continue;
ProjectDescription description = project.internalGetDescription();
if (description == null)
continue;
IProject[] references = description.getAllReferences(project, false);
for (IProject reference : references)
if (reference.equals(this)) {
result.add(project);
break;
}
}
return result.toArray(new IProject[result.size()]);
}
@Override
public int getType() {
return PROJECT;
}
@Override
public IPath getWorkingLocation(String id) {
if (id == null || !exists())
return null;
IPath result = workspace.getMetaArea().getWorkingLocation(this, id);
result.toFile().mkdirs();
return result;
}
@Override
public boolean hasBuildConfig(String configName) throws CoreException {
ProjectInfo info = (ProjectInfo) getResourceInfo(false, false);
checkAccessible(getFlags(info));
return internalHasBuildConfig(configName);
}
@Override
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 IBuildConfiguration config, final int trigger, final String builderName, final Map args, IProgressMonitor monitor) throws CoreException {
ICoreRunnable buildRunnable = new ICoreRunnable() {
@Override
public void run(IProgressMonitor innerMonitor) throws CoreException {
final ISchedulingRule projectBuildRule = workspace.getBuildManager().getRule(config, trigger,
builderName, args);
final boolean relaxed = Job.getJobManager().currentRule() == null && workspace.isRelaxedRule(projectBuildRule);
// PRE + POST_BUILD, and the build itself are allowed to modify resources, so require the current thread's scheduling rule
// to either contain the WR or be null. Therefore, if not null, ensure it contains the WR rule...
final ISchedulingRule notificationsRule = relaxed ? null : workspace.getRoot();
SubMonitor subMonitor = SubMonitor.convert(innerMonitor, 100);
try {
try {
workspace.prepareOperation(notificationsRule, innerMonitor);
if (!shouldBuild())
return;
workspace.beginOperation(true);
workspace.aboutToBuild(Project.this, trigger);
} finally {
workspace.endOperation(notificationsRule, false);
}
try {
IStatus result;
workspace.prepareOperation(projectBuildRule, innerMonitor);
// don't open the tree eagerly because it will be wasted if no build occurs
workspace.beginOperation(false);
result = workspace.getBuildManager().build(config, trigger, builderName, args,
subMonitor.split(100));
if (!result.isOK())
throw new ResourceException(result);
} finally {
workspace.endOperation(projectBuildRule, false);
try {
workspace.prepareOperation(notificationsRule, 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(notificationsRule, false);
}
}
} finally {
subMonitor.done();
}
}
/**
* Returns whether this project should be built for a given trigger.
* @return true
if the build should proceed, and false
otherwise.
*/
private boolean shouldBuild() {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
if (!exists(flags, true) || !isOpen(flags)) {
return false;
}
return true;
}
};
workspace.run(buildRunnable, 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(IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
workspace.flushBuildOrder();
getMarkerManager().removeMarkers(this, IResource.DEPTH_INFINITE);
subMonitor.worked(1);
// 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);
subMonitor.setWorkRemaining(members.length);
for (IResource member2 : members) {
Resource member = (Resource) member2;
workspace.deleteResource(member);
subMonitor.worked(1);
}
// finally mark the project as closed.
ResourceInfo info = getResourceInfo(false, true);
info.clear(M_OPEN);
info.clearSessionProperties();
info.clearModificationStamp();
info.clearCharsetGenerationCount();
info.setSyncInfo(null);
}
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 = IPath.fromOSString(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, destDesc, 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);
}
} finally {
monitor.done();
}
}
/*
* Copies just the project and no children. Does NOT copy the meta area.
*/
protected void internalCopyProjectOnly(IResource destination, IProjectDescription destDesc, 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);
//copy the hidden metadata that we store in the project description
ProjectDescription projectDesc = (ProjectDescription) destDesc;
ProjectDescription internalDesc = ((Project) destination.getProject()).internalGetDescription();
projectDesc.setLinkDescriptions(internalDesc.getLinks());
projectDesc.setFilterDescriptions(internalDesc.getFilters());
projectDesc.setVariableDescriptions(internalDesc.getVariables());
//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();
}
/**
* Like {@link #getActiveBuildConfig()} but doesn't check accessibility.
* Project must be accessible.
* @see #getActiveBuildConfig()
*/
IBuildConfiguration internalGetActiveBuildConfig() {
String configName = internalGetDescription().activeConfiguration;
try {
if (configName != null)
return getBuildConfig(configName);
} catch (CoreException e) {
// Build configuration doesn't exist; treat the first as active.
}
return internalGetBuildConfigs(false)[0];
}
/**
* @return IBuildConfiguration[] always contains at least one build configuration
*/
public IBuildConfiguration[] internalGetBuildConfigs(boolean makeCopy) {
ProjectDescription desc = internalGetDescription();
if (desc == null)
return new IBuildConfiguration[] {new BuildConfiguration(this, IBuildConfiguration.DEFAULT_CONFIG_NAME)};
return desc.getBuildConfigs(this, makeCopy);
}
/**
* 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();
}
/**
* Returns the IBuildConfigurations referenced by the passed in build configuration
* @param configName to find references for
* @return IBuildConfiguration[] of referenced configurations; never null.
*/
public IBuildConfiguration[] internalGetReferencedBuildConfigs(String configName, boolean includeMissing) {
ProjectDescription description = internalGetDescription();
IBuildConfiguration[] refs = description.getAllBuildConfigReferences(this, configName, false);
Collection configs = new LinkedHashSet<>(refs.length);
for (IBuildConfiguration ref : refs) {
try {
configs.add((((BuildConfiguration) ref).getBuildConfig()));
} catch (CoreException e) {
// The project isn't accessible, or the build configuration doesn't exist
// on the project. If requested return the full set of build references which may
// be useful to API consumers
if (includeMissing)
configs.add(ref);
}
}
return configs.toArray(new IBuildConfiguration[configs.size()]);
}
boolean internalHasBuildConfig(String configName) {
return internalGetDescription().hasBuildConfig(configName);
}
/**
* 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) {
// Project has been added / removed. Build order is out-of-step
workspace.flushBuildOrder();
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);
}
}
@Override
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 (IResource element : children)
((Resource) element).internalSetLocal(flag, depth);
}
@Override
public boolean isAccessible() {
return isOpen();
}
@Override
public boolean isDerived(int options) {
//projects are never derived
return false;
}
@Override
public boolean isLinked(int options) {
return false;//projects are never linked
}
@Override
public boolean isVirtual() {
return false; // projects are never virtual
}
@Override
public boolean isTeamPrivateMember(int options) {
return false;//projects are never team private members
}
@Deprecated
@Override
public boolean isLocal(int depth) {
// the flags parameter is ignored for projects so pass anything
return isLocal(-1, depth);
}
@Deprecated
@Override
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 (IResource element : children)
if (!element.isLocal(depth))
return false;
return true;
}
@Override
public boolean isNatureEnabled(String natureId) throws CoreException {
checkAccessible(getFlags(getResourceInfo(false, false)));
return workspace.getNatureManager().isNatureEnabled(this, natureId);
}
@Override
public boolean isOpen() {
ResourceInfo info = getResourceInfo(false, false);
return isOpen(getFlags(info));
}
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);
}
@Override
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);
}
}
@Override
public void move(IProjectDescription destination, boolean force, IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(destination);
move(destination, force ? IResource.FORCE : IResource.NONE, monitor);
}
@Override
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);
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 = IPath.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());
// make sure the move operation is remembered
workspace.getSaveManager().requestSnapshot();
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true);
}
} finally {
monitor.done();
}
}
@Override
public void open(int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor = Policy.monitorFor(monitor);
boolean encodingWritten = false;
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));
}
}
} else {
// Bug 544975 - When opening a closed project, refresh it in the background
if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) {
workspace.refreshManager.refresh(this);
monitor.worked(Policy.opWork * 60 / 100);
}
}
// Project is new and does not have any content already (not imported)
if (!used && !unknownChildren) {
writeEncodingAfterOpen(monitor);
encodingWritten = true;
}
//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);
}
} finally {
monitor.done();
}
if (!encodingWritten) {
ValidateProjectEncoding.scheduleProjectValidation((Workspace) getWorkspace(), this);
}
}
/**
* Try to set encoding if we open the project for the first time. See bug 479450
*/
private void writeEncodingAfterOpen(IProgressMonitor monitor) throws CoreException {
IPath settings = IPath.fromOSString(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME).append(ResourcesPlugin.PI_RESOURCES)
.addFileExtension(EclipsePreferences.PREFS_FILE_EXTENSION);
IFile file = getFile(settings);
// The file could not yet be up-to-date with underlined resource
// force refresh to force reading project preferences via
// org.eclipse.core.internal.resources.ProjectPreferences.updatePreferences(IFile)
IPath location = file.getLocation();
if (!file.exists() && location != null && location.toFile().exists()) {
file.refreshLocal(IResource.DEPTH_ZERO, monitor);
}
String charset = workspace.getCharsetManager().getCharsetFor(getFullPath(), false);
if (charset == null) {
String encoding = ResourcesPlugin.getEncoding();
workspace.getCharsetManager().setCharsetFor(getFullPath(), encoding);
}
}
@Override
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 (LinkDescription oldLink : oldLinks.values()) {
Resource oldLinkResource = (Resource) findMember(oldLink.getProjectRelativePath());
if (oldLinkResource == null || !oldLinkResource.isLinked())
continue;
LinkDescription newLink = null;
if (newLinks != null)
newLink = 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<>((arg0, arg1) -> {
int numberOfSegments0 = arg0.getProjectRelativePath().segmentCount();
int numberOfSegments1 = 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 (LinkDescription newLink : newLinksAndGroups) {
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;
}
@Override
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();
}
}
@Override
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.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.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.POST_PROJECT_CHANGE, this));
workspace.endOperation(rule, true);
}
} finally {
monitor.done();
}
}
@Override
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));
}
@Override
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.beginOperation(true);
super.touch(Policy.subMonitorFor(monitor, Policy.opWork));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
try {
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.POST_PROJECT_CHANGE, this));
} finally {
workspace.endOperation(rule, true);
}
}
} 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 {
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 {
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.POST_PROJECT_CHANGE, this));
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;
}
}
@Override
public String getDefaultLineSeparator() {
Preferences rootNode = Platform.getPreferencesService().getRootNode();
// if the file does not exist or has no content yet, try with project preference
String value = getLineSeparatorFromPreferences(rootNode.node(ProjectScope.SCOPE).node(getProject().getName()));
if (value != null)
return value;
// try with instance preferences
value = getLineSeparatorFromPreferences(rootNode.node(InstanceScope.SCOPE));
if (value != null)
return value;
// try with default preferences
value = getLineSeparatorFromPreferences(rootNode.node(DefaultScope.SCOPE));
if (value != null)
return value;
// if there is no preference set, fall back to OS default value
return System.lineSeparator();
}
private static String getLineSeparatorFromPreferences(Preferences node) {
try {
// be careful looking up for our node so not to create any nodes as side effect
if (node.nodeExists(Platform.PI_RUNTIME))
return node.node(Platform.PI_RUNTIME).get(Platform.PREF_LINE_SEPARATOR, null);
} catch (org.osgi.service.prefs.BackingStoreException e) {
// ignore
}
return null;
}
}