Skip to content

Commit

Permalink
Fix stale resource link flags after project re-open
Browse files Browse the repository at this point in the history
Whenever a project is closed, its resource tree is saved. This includes
linked resources in the project. When the project is re-opened, link
changes in the .project file are not reflected on the projects resource
tree. The old resource tree is read, new information is stored in
ProjectDescription.linkDescriptions, but the old linked resources are
not touched.

This change adjusts Project.open() and Project.close() to set resp.
clear the M_LINK flag of linked resources in the project.

Fixes: #470
Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
  • Loading branch information
trancexpress authored and iloveeclipse committed Jun 14, 2023
1 parent 39d33ea commit 13b3ddd
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.net.URI;
import java.util.*;
import java.util.function.Consumer;
import org.eclipse.core.filesystem.*;
import org.eclipse.core.internal.events.LifecycleEvent;
import org.eclipse.core.internal.preferences.EclipsePreferences;
Expand Down Expand Up @@ -199,6 +200,11 @@ public void close(IProgressMonitor monitor) throws CoreException {
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, this));
// flush the build order early in case there is a problem
workspace.flushBuildOrder();
// Clear the linked flag of linked resources,
// since changes to the links in .project while the project is closed
// are otherwise not reflected after re-opening the project.
// See: https://github.com/eclipse-platform/eclipse.platform/issues/470
clearLinkFlags();
IProgressMonitor sub = subMonitor.newChild(49, SubMonitor.SUPPRESS_SUBTASK);
IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub);
internalClose(subMonitor.newChild(49));
Expand Down Expand Up @@ -1107,6 +1113,9 @@ public void open(int updateFlags, IProgressMonitor monitor) throws CoreException
writeEncodingAfterOpen(monitor);
encodingWritten = true;
}
if (used) {
setLinkFlags();
}
//creation of this project may affect overlapping resources
workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_INFINITE, monitor);
} catch (OperationCanceledException e) {
Expand Down Expand Up @@ -1446,6 +1455,36 @@ public String getDefaultLineSeparator() {
return System.lineSeparator();
}

/**
* Clears the {@link ICoreConstants#M_LINK} flag of linked resources.
*/
private void clearLinkFlags() {
modifyLinksResourceInfo(info -> info.clear(M_LINK));
}

/**
* Sets the {@link ICoreConstants#M_LINK} flag of linked resources.
*/
private void setLinkFlags() {
modifyLinksResourceInfo(info -> info.set(M_LINK));
}

private void modifyLinksResourceInfo(Consumer<ResourceInfo> operation) {
ProjectDescription description = internalGetDescription();
HashMap<IPath, LinkDescription> linkDescriptions = description.linkDescriptions;
if (linkDescriptions != null) {
for (LinkDescription linkDescription : linkDescriptions.values()) {
IFile linkFile = getFile(linkDescription.getProjectRelativePath());
if (linkFile != null) {
ResourceInfo linkInfo = workspace.getResourceInfo(linkFile.getFullPath(), false, true);
if (linkInfo != null) {
operation.accept(linkInfo);
}
}
}
}
}

private static String getLineSeparatorFromPreferences(Preferences node) {
try {
// be careful looking up for our node so not to create any nodes as side effect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
LinkedResourceWithPathVariableTest.class, LinkedResourceSyncMoveAndCopyTest.class, MarkerSetTest.class,
MarkerTest.class, NatureTest.class, NonLocalLinkedResourceTest.class, ProjectEncodingTest.class,
ProjectOrderTest.class,
ProjectLinksTest.class,
ProjectScopeTest.class, ProjectSnapshotTest.class, ResourceAttributeTest.class, ResourceURLTest.class,
TeamPrivateMemberTest.class, WorkspaceTest.class })
public class AllResourcesTests {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2023 Simeon Andreev 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:
* Simeon Andreev - initial API and implementation
*******************************************************************************/
package org.eclipse.core.tests.resources;

import java.nio.file.*;
import java.util.List;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.IPath;

/**
*
*/
public class ProjectLinksTest extends ResourceTest {

private IProject project;
private Path tmpFolder;
private IPath tmpPath;

@Override
protected void setUp() throws Exception {
super.setUp();
tmpPath = getRandomLocation();
tmpFolder = Paths.get(tmpPath.toOSString());
Files.createDirectory(tmpFolder);

IWorkspaceRoot root = getWorkspace().getRoot();
project = root.getProject(getUniqueString());

project.create(getMonitor());
project.open(getMonitor());
project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
}

@Override
protected void tearDown() throws Exception {
try {
Files.deleteIfExists(tmpFolder);
project.delete(true, getMonitor());
} finally {
super.tearDown();
}
}

/**
* Tests that link information is updated after closing a project, deleting a
* link in the {@code .project} file and then opening the project.
*/
public void testCloseProjectDeleteLinksAndOpen_GH470() throws Exception {
IFile dotProject = project.getFile(".project");
Path dotProjectPath = Paths.get(dotProject.getLocationURI());
List<String> dotProjectContentsWithoutLink = Files.readAllLines(dotProjectPath);

String linkedFolderName = "test";
IFolder folder = project.getFolder(linkedFolderName);
folder.createLink(tmpPath, IResource.NONE, getMonitor());
project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());

assertTrue("Failed to create linked folder in test project", folder.isLinked());

project.close(getMonitor());

Files.write(dotProjectPath, dotProjectContentsWithoutLink);

project.open(getMonitor());
project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());

folder = project.getFolder(linkedFolderName);
assertFalse("Expected folder to not be linked after re-opening project", folder.isLinked());
}

/**
* Tests that link information is correct after closing a project and then
* opening the project.
*/
public void testCloseAndOpenProject() throws Exception {
String linkedFolderName = "test";
IFolder folder = project.getFolder(linkedFolderName);
folder.createLink(tmpPath, IResource.NONE, getMonitor());
project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());

assertTrue("Failed to create linked folder in test project", folder.isLinked());

project.close(getMonitor());

project.open(getMonitor());
project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());

folder = project.getFolder(linkedFolderName);
assertTrue("Expected folder to be linked after re-opening project", folder.isLinked());
}
}

0 comments on commit 13b3ddd

Please sign in to comment.