Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,12 @@ protected boolean refreshResource(IResource target, int depth, boolean updateAli
UnifiedTree tree = fileTree == null ? new UnifiedTree(target) : new UnifiedTree(target, fileTree);
SubMonitor refreshMonitor = subMonitor.newChild(98);
RefreshLocalVisitor visitor = updateAliases ? new RefreshLocalAliasVisitor(refreshMonitor) : new RefreshLocalVisitor(refreshMonitor);
tree.accept(visitor, depth);
try {
tree.accept(visitor, depth);
} finally {
// the description file is gone from the tree even when the refresh was canceled
visitor.closeProjectsWithoutDescription();
}
IStatus result = visitor.getErrorStatus();
if (!result.isOK()) {
throw new ResourceException(result);
Expand Down Expand Up @@ -1476,42 +1481,6 @@ public void write(IFolder target, boolean force, IProgressMonitor monitor) throw
updateLocalSync(info, store.fetchInfo().getLastModified());
}

/**
Comment thread
vogella marked this conversation as resolved.
* Write the .project file without modifying the resource tree. This is called
* during save when it is discovered that the .project file is missing. The tree
* cannot be modified during save.
*/
public void writeSilently(IProject target) throws CoreException {
IPath location = locationFor(target, false);
//if the project location cannot be resolved, we don't know if a description file exists or not
if (location == null) {
return;
}
IFileStore projectStore = getStore(target);
projectStore.mkdir(EFS.NONE, null);
//can't do anything if there's no description
IProjectDescription desc = ((Project) target).internalGetDescription();
if (desc == null) {
return;
}
//write the project's private description to the meta-data area
getWorkspace().getMetaArea().writePrivateDescription(target);

//write the file that represents the project description
IFileStore fileStore = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
try (
OutputStream out = fileStore.openOutputStream(EFS.NONE, null)
) {
IFile file = target.getFile(IProjectDescription.DESCRIPTION_FILE_NAME);
new ModelObjectWriter().write(desc, out, file.getLineSeparator(true));
} catch (IOException e) {
String msg = NLS.bind(Messages.resources_writeMeta, target.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), msg, e);
}
//for backwards compatibility, ensure the old .prj file is deleted
getWorkspace().getMetaArea().clearOldDescription(target);
}

public boolean storeHistory(IResource file) {
WorkspaceDescription description = workspace.internalGetDescription();
return (description.isKeepDerivedState() || !file.isDerived()) && !disableHistory(file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
*******************************************************************************/
package org.eclipse.core.internal.localstore;

import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.core.internal.resources.Container;
import org.eclipse.core.internal.resources.File;
import org.eclipse.core.internal.resources.Folder;
import org.eclipse.core.internal.resources.ICoreConstants;
import org.eclipse.core.internal.resources.Project;
import org.eclipse.core.internal.resources.Resource;
import org.eclipse.core.internal.resources.ResourceInfo;
import org.eclipse.core.internal.resources.ResourceStatus;
Expand Down Expand Up @@ -56,6 +59,7 @@ public class RefreshLocalVisitor implements IUnifiedTreeVisitor, ILocalStoreCons
protected SubMonitor monitor;
protected boolean resourceChanged;
protected Workspace workspace;
private final Set<Project> projectsWithoutDescription = new LinkedHashSet<>();

public RefreshLocalVisitor(IProgressMonitor monitor) {
this.monitor = SubMonitor.convert(monitor);
Expand Down Expand Up @@ -107,6 +111,13 @@ protected void deleteResource(UnifiedTreeNode node, Resource target) throws Core
}
if (target.exists(flags, false)) {
target.deleteResource(true, errors);
// a filtered description file is still read from disk
if (target.getType() == IResource.FILE && ((File) target).isProjectDescriptionFile()) {
Project project = (Project) target.getProject();
if (project.isOpen() && !target.getLocalManager().hasSavedDescription(project)) {
projectsWithoutDescription.add(project);
}
}
}
node.setExistsWorkspace(false);
}
Expand Down Expand Up @@ -146,6 +157,29 @@ protected void folderToFile(UnifiedTreeNode node, Resource target) throws CoreEx
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}

/**
* Closes the open projects whose description file was found deleted, rather
* than recreating the file. Failures are added to the error status.
*/
public void closeProjectsWithoutDescription() {
for (Project project : projectsWithoutDescription) {
closeProjectWithoutDescription(project);
}
}

private void closeProjectWithoutDescription(Project project) {
if (!project.isOpen()) {
return;
}
String message = NLS.bind(Messages.resources_missingProjectMetaClosed, project.getName());
Policy.log(new ResourceStatus(IStatus.WARNING, IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), message, null));
try {
project.basicClose(null);
} catch (CoreException e) {
errors.merge(e.getStatus());
}
}

/**
* Returns the status of the nodes visited so far. This will be a multi-status
* that describes all problems that have occurred, or an OK status if everything
Expand Down Expand Up @@ -287,6 +321,12 @@ public boolean visit(UnifiedTreeNode node) throws CoreException {
Resource target = (Resource) node.getResource();
int targetType = target.getType();
if (targetType == IResource.PROJECT) {
// close before visiting the members, whose deletion could write the description back
Project project = (Project) target;
if (project.isOpen() && !target.getLocalManager().hasSavedDescription(project)) {
closeProjectWithoutDescription(project);
return false;
}
return true;
}
if (node.existsInWorkspace() && node.existsInFileSystem()) {
Expand Down Expand Up @@ -318,7 +358,8 @@ public boolean visit(UnifiedTreeNode node) throws CoreException {
}
int state = synchronizeExistence(node, target);
if (state == RL_IN_SYNC || state == RL_NOT_IN_SYNC) {
if (targetType == IResource.FILE) {
// the metadata of a project about to be closed is not read
if (targetType == IResource.FILE && !projectsWithoutDescription.contains(target.getProject())) {
try {
((File) target).updateMetadataFiles();
} catch (CoreException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ public void setContents(byte[] content, int updateFlags, IProgressMonitor monito
public long setLocalTimeStamp(long value) throws CoreException {
//override to handle changing timestamp on project description file
long result = super.setLocalTimeStamp(value);
if (path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
if (isProjectDescriptionFile()) {
//handle concurrent project deletion
ResourceInfo projectInfo = ((Project) getProject()).getResourceInfo(false, false);
if (projectInfo != null) {
Expand All @@ -576,10 +576,7 @@ public long setLocalTimeStamp(long value) throws CoreException {
* been modified (added, removed, or changed).
*/
public void updateMetadataFiles() throws CoreException {
int count = path.segmentCount();
String name = path.segment(1);
// is this a project description file?
if (count == 2 && name.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
if (isProjectDescriptionFile()) {
Project project = (Project) getProject();
project.updateDescription();
// Discard stale project natures on ProjectInfo
Expand All @@ -588,12 +585,19 @@ public void updateMetadataFiles() throws CoreException {
return;
}
// check to see if we are in the .settings directory
if (count == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(name)) {
if (path.segmentCount() == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1))) {
ProjectPreferences.updatePreferences(this);
return;
}
}

/**
* Returns whether this file is the description file (.project) of its project.
*/
public boolean isProjectDescriptionFile() {
return path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME);
}

@Deprecated
@Override
public void setCharset(String newCharset) throws CoreException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,8 @@ public void close(IProgressMonitor monitor) throws CoreException {
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);
}
basicClose(subMonitor.newChild(98));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
Expand Down Expand Up @@ -670,6 +659,27 @@ private boolean shouldBuild() {
workspace.run(buildRunnable, null, IWorkspace.AVOID_UPDATE, monitor);
}

/**
* Closes this open project. Must be called from within a workspace operation;
* no further scheduling rule is acquired, since a nested project may be closed
* under the rule of the project that contains it.
*/
public void basicClose(IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
// 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.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(1, SubMonitor.SUPPRESS_SUBTASK);
IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, false, this, null, sub);
internalClose(subMonitor.newChild(1));
if (saveStatus != null && !saveStatus.isOK()) {
throw new ResourceException(saveStatus);
}
}

/**
* 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,13 +800,15 @@ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreExcepti
progress.subTask(message);
final ISchedulingRule rule = workspace.getRuleFactory().deleteRule(this);
SubMonitor split = progress.split(1);
boolean deletesDescriptionFile = false;
try {
workspace.prepareOperation(rule, split);
// If there is no resource then there is nothing to delete so just return.
if (!exists()) {
return;
}
workspace.beginOperation(true);
deletesDescriptionFile = getType() == FILE && ((File) this).isProjectDescriptionFile();
broadcastPreDeleteEvent();

// When a project is being deleted, flush the build order in case there is a problem.
Expand Down Expand Up @@ -841,7 +843,8 @@ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreExcepti
// Update any aliases of this resource.
// Note that deletion of a linked resource cannot affect other resources.
if (!wasLinked) {
workspace.getAliasManager().updateAliases(this, originalStore, IResource.DEPTH_INFINITE, progress.split(48));
// not cancelable, the aliases have to follow the deletion done on disk
workspace.getAliasManager().updateAliases(this, originalStore, IResource.DEPTH_INFINITE, progress.newChild(48));
}
if (getType() == PROJECT) {
// Make sure the rule factory is cleared on project deletion.
Expand All @@ -853,8 +856,36 @@ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreExcepti
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
progress.done();
workspace.endOperation(rule, true);
try {
// also when canceled or failed after the file was deleted
if (deletesDescriptionFile) {
closeProjectWithoutDescription();
}
} finally {
progress.done();
workspace.endOperation(rule, true);
}
}
}

/**
* Closes the open project of this description file if its description file
* is gone, rather than recreating the file.
*/
private void closeProjectWithoutDescription() throws CoreException {
Project project = (Project) getProject();
if (project.isOpen() && !getLocalManager().hasSavedDescription(project)) {
project.basicClose(null);
}
}

/**
* Writes the description of the given project unless its description file is
* gone, since such a project gets closed instead of recreating the file.
*/
private void writeDescriptionIfSaved(Project project) throws CoreException {
if (getLocalManager().hasSavedDescription(project)) {
project.writeDescription(IResource.FORCE);
}
}

Expand Down Expand Up @@ -908,7 +939,7 @@ public void deleteResource(boolean convertToPhantom, MultiStatus status) throws
if (wasChanged) {
project.internalSetDescription(description, true);
try {
project.writeDescription(IResource.FORCE);
writeDescriptionIfSaved(project);
} catch (CoreException e) {
// A problem happened updating the description, update the description in memory.
project.updateDescription();
Expand Down Expand Up @@ -936,7 +967,7 @@ public void deleteResource(boolean convertToPhantom, MultiStatus status) throws
description.setFilters(resource.getProjectRelativePath(), null);
}
project.internalSetDescription(description, true);
project.writeDescription(IResource.FORCE);
writeDescriptionIfSaved(project);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1114,27 +1114,32 @@ public void standardMoveProject(IProject source, IProjectDescription description
}

// Move the project content in the local file system.
boolean contentMoved = true;
try {
moveProjectContent(source, destinationStore, flags, Policy.subMonitorFor(monitor, Policy.totalWork * 3 / 4));
} catch (CoreException e) {
message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
failed(status);
//refresh the project because it might have been partially moved
try {
source.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e2) {
//ignore secondary failures
}
contentMoved = false;
}

// If we got this far the project content has been moved on disk (if necessary)
// and we need to update the workspace tree.
movedProjectSubtree(source, description);
monitor.worked(Policy.totalWork * 1 / 8);

IProject destination = source.getWorkspace().getRoot().getProject(description.getName());
if (!contentMoved) {
// the content might have been partially moved, so align the tree with the destination
try {
destination.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
//ignore secondary failures
}
}
boolean isDeep = (flags & IResource.SHALLOW) == 0;
updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()), isDeep);
updateTimestamps(destination, isDeep);
monitor.worked(Policy.totalWork * 1 / 8);
} finally {
lock.release();
Expand Down
Loading
Loading