Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix stale resource link flags after project re-open #473

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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 @@ -29,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.function.Consumer;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
Expand Down Expand Up @@ -232,6 +233,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 @@ -1149,6 +1155,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 @@ -1488,6 +1497,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 @@ -52,6 +52,7 @@
TeamPrivateMemberTest.class, //
VirtualFolderTest.class, //
WorkspaceTest.class, //
ProjectLinksTest.class, //
})
public class AllResourcesTests {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*******************************************************************************
* 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 static org.eclipse.core.tests.harness.FileSystemHelper.getRandomLocation;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.tests.harness.FussyProgressMonitor;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
/**
*
*/
public class ProjectLinksTest {

@Rule
public WorkspaceTestRule workspaceRule = new WorkspaceTestRule();

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

@Before
public void setUp() throws Exception {
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());
}

@After
public void tearDown() throws Exception {
Files.deleteIfExists(tmpFolder);
project.delete(true, getMonitor());
}

/**
* Tests that link information is updated after closing a project, deleting a
* link in the {@code .project} file and then opening the project.
*/
@Test
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());
}

static IProgressMonitor getMonitor() {
return new FussyProgressMonitor();
}

public static IWorkspace getWorkspace() {
return ResourcesPlugin.getWorkspace();
}

public String getUniqueString() {
return System.nanoTime() + "-" + Math.random();
}

}
Loading