diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java
index e39ab8a650d..6a84fba1f07 100644
--- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java
+++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2025 IBM Corporation and others.
+ * Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -372,20 +372,28 @@ private IResource resourceForLocation(IPath location, boolean files) {
}
return null;
}
+ int locationSegments = location.segmentCount();
int resultProjectPathSegments = 0;
IResource result = null;
- IProject[] projects = getWorkspace().getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
- for (IProject project : projects) {
+ for (IProject project : workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN)) {
IPath projectLocation = project.getLocation();
- if (projectLocation != null && projectLocation.isPrefixOf(location)) {
- int segmentsToRemove = projectLocation.segmentCount();
- if (segmentsToRemove > resultProjectPathSegments) {
- IPath path = project.getFullPath().append(location.removeFirstSegments(segmentsToRemove));
- IResource resource = resourceFor(path, files);
- if (resource != null && !((Resource) resource).isFiltered()) {
- resultProjectPathSegments = segmentsToRemove;
- result = resource;
- }
+ if (projectLocation == null) {
+ continue;
+ }
+ int segmentsToRemove = projectLocation.segmentCount();
+ // cheap segment count checks first, the prefix check is more expensive
+ if (segmentsToRemove <= resultProjectPathSegments || segmentsToRemove > locationSegments
+ || !projectLocation.isPrefixOf(location)) {
+ continue;
+ }
+ IPath path = project.getFullPath().append(location.removeFirstSegments(segmentsToRemove));
+ IResource resource = resourceFor(path, files);
+ if (resource != null && !((Resource) resource).isFiltered()) {
+ resultProjectPathSegments = segmentsToRemove;
+ result = resource;
+ if (segmentsToRemove == locationSegments) {
+ // no other project can have a longer matching location
+ break;
}
}
}
diff --git a/resources/tests/org.eclipse.core.tests.resources/META-INF/MANIFEST.MF b/resources/tests/org.eclipse.core.tests.resources/META-INF/MANIFEST.MF
index 67609896715..ca5940c0488 100644
--- a/resources/tests/org.eclipse.core.tests.resources/META-INF/MANIFEST.MF
+++ b/resources/tests/org.eclipse.core.tests.resources/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Eclipse Core Tests Resources
Bundle-SymbolicName: org.eclipse.core.tests.resources; singleton:=true
-Bundle-Version: 3.11.1400.qualifier
+Bundle-Version: 3.24.200.qualifier
Bundle-Vendor: Eclipse.org
Export-Package: org.eclipse.core.tests.filesystem,
org.eclipse.core.tests.internal.alias,
diff --git a/resources/tests/org.eclipse.core.tests.resources/pom.xml b/resources/tests/org.eclipse.core.tests.resources/pom.xml
index 2242d27669e..865de202b26 100644
--- a/resources/tests/org.eclipse.core.tests.resources/pom.xml
+++ b/resources/tests/org.eclipse.core.tests.resources/pom.xml
@@ -18,7 +18,7 @@
4.42.0-SNAPSHOT
org.eclipse.core.tests.resources
- 3.11.1400-SNAPSHOT
+ 3.24.200-SNAPSHOT
eclipse-test-plugin
diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IWorkspaceRootTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IWorkspaceRootTest.java
index ff799d7cd58..d0e7cf05f36 100644
--- a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IWorkspaceRootTest.java
+++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IWorkspaceRootTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2023 IBM Corporation and others.
+ * Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -34,12 +34,14 @@
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.resources.FileInfoMatcherDescription;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceFilterDescription;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
@@ -414,6 +416,135 @@ public void testBug476585() throws CoreException {
assertEquals(subProject, container.getProject());
}
+ /**
+ * The location of a project itself maps to the project as container and to
+ * no file.
+ */
+ @Test
+ public void testGetForLocationOfProjectLocation() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ IProject project = root.getProject("p");
+ createInWorkspace(project);
+
+ assertEquals(project, root.getContainerForLocation(project.getLocation()));
+ assertNull(root.getFileForLocation(project.getLocation()));
+ }
+
+ /**
+ * Locations outside of any project do not map to any resource.
+ */
+ @Test
+ public void testGetForLocationOutsideOfProjects() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ IProject project = root.getProject("p");
+ createInWorkspace(project);
+
+ IPath outside = root.getLocation().append("notAProject").append("file.txt");
+ assertNull(root.getFileForLocation(outside));
+ assertNull(root.getContainerForLocation(outside));
+ }
+
+ /**
+ * A project location must be a segment-wise prefix of the given location, a
+ * plain string prefix (e.g. "p" for "p2") must not match.
+ */
+ @Test
+ public void testGetForLocationWithStringPrefixProjectName() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ IProject project = root.getProject("p");
+ IProject project2 = root.getProject("p2");
+ createInWorkspace(new IResource[] { project, project2 });
+
+ IPath fileLocation = project2.getLocation().append("file.txt");
+ assertEquals(project2.getFile("file.txt"), root.getFileForLocation(fileLocation));
+ assertEquals(project2.getFolder("file.txt"), root.getContainerForLocation(fileLocation));
+ }
+
+ /**
+ * The nested project wins regardless of the order in which projects are
+ * iterated (see also {@link #testBug476585()} for the opposite order).
+ */
+ @Test
+ public void testGetForLocationNestedProjectIteratedFirst() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ IProject outer = root.getProject("z_outer");
+ createInWorkspace(outer);
+ IProject inner = createProjectAt("a_inner", outer.getLocation().append("nested"));
+
+ IPath fileLocation = inner.getLocation().append("folder").append("file.txt");
+ assertEquals(inner.getFile("folder/file.txt"), root.getFileForLocation(fileLocation));
+ assertEquals(inner.getFolder("folder/file.txt"), root.getContainerForLocation(fileLocation));
+ }
+
+ /**
+ * The location of a nested project maps to the nested project as container,
+ * but to a file of the enclosing project, since a project location can never
+ * be a file of the nested project itself.
+ */
+ @Test
+ public void testGetForLocationOfNestedProjectLocation() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ for (String[] names : new String[][] { { "a_outer", "z_inner" }, { "z_outer", "a_inner" } }) {
+ IProject outer = root.getProject(names[0]);
+ createInWorkspace(outer);
+ IProject inner = createProjectAt(names[1], outer.getLocation().append("nested"));
+
+ assertEquals(inner, root.getContainerForLocation(inner.getLocation()));
+ assertEquals(outer.getFile("nested"), root.getFileForLocation(inner.getLocation()));
+
+ removeFromWorkspace(new IResource[] { inner, outer });
+ }
+ }
+
+ /**
+ * If the resource in the nested project is filtered out, the resource of the
+ * enclosing project is returned instead.
+ */
+ @Test
+ public void testGetForLocationFilteredInNestedProject() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ for (String[] names : new String[][] { { "a_outer", "z_inner" }, { "z_outer", "a_inner" } }) {
+ IProject outer = root.getProject(names[0]);
+ createInWorkspace(outer);
+ IProject inner = createProjectAt(names[1], outer.getLocation().append("nested"));
+ inner.createFilter(
+ IResourceFilterDescription.EXCLUDE_ALL | IResourceFilterDescription.FOLDERS
+ | IResourceFilterDescription.FILES | IResourceFilterDescription.INHERITABLE,
+ new FileInfoMatcherDescription("org.eclipse.core.resources.regexFilterMatcher", "folder"), 0,
+ createTestMonitor());
+
+ IPath fileLocation = inner.getLocation().append("folder").append("file.txt");
+ assertEquals(outer.getFile("nested/folder/file.txt"), root.getFileForLocation(fileLocation));
+ assertEquals(outer.getFolder("nested/folder/file.txt"), root.getContainerForLocation(fileLocation));
+
+ removeFromWorkspace(new IResource[] { inner, outer });
+ }
+ }
+
+ /**
+ * Files and folders of hidden projects are found as well.
+ */
+ @Test
+ public void testGetForLocationInHiddenProject() throws CoreException {
+ IWorkspaceRoot root = getWorkspace().getRoot();
+ IProject hiddenProject = root.getProject(createUniqueString());
+ hiddenProject.create(null, IResource.HIDDEN, createTestMonitor());
+ hiddenProject.open(createTestMonitor());
+
+ IPath fileLocation = hiddenProject.getLocation().append("file.txt");
+ assertEquals(hiddenProject.getFile("file.txt"), root.getFileForLocation(fileLocation));
+ assertEquals(hiddenProject.getFolder("file.txt"), root.getContainerForLocation(fileLocation));
+ }
+
+ private IProject createProjectAt(String name, IPath location) throws CoreException {
+ IProject project = getWorkspace().getRoot().getProject(name);
+ IProjectDescription description = getWorkspace().newProjectDescription(name);
+ description.setLocation(location);
+ project.create(description, createTestMonitor());
+ project.open(createTestMonitor());
+ return project;
+ }
+
/*
* see bug 232765 for details
*/