Skip to content

Commit f19201c

Browse files
committed
Add Java projects to classpath container org.eclipse.pde.core.externalJavaSearch
The classpath container org.eclipse.pde.core.externalJavaSearch skips adding plug-ins if a respective project is found in the workspace. This can result in unexpected behavior when browsing code from the container, such as wrong syntax highlighting, navigation and search results. This change adjusts the classpath container to contain also projects found in the workspace. A test is also added for the initial bug fix, due to which workspace projects are skipped: https://bugs.eclipse.org/bugs/show_bug.cgi?id=197817 Fixes: #2269
1 parent 12fa5d0 commit f19201c

3 files changed

Lines changed: 148 additions & 2 deletions

File tree

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/SearchablePluginsManager.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,16 @@ public IClasspathEntry[] computeContainerClasspathEntries() {
183183
for (String id : plugins) {
184184
ModelEntry entry = PluginRegistry.findEntry(id);
185185
if (entry != null) {
186-
boolean addModel = Arrays.stream(entry.getWorkspaceModels())
186+
List<IProject> javaProjects = Arrays.stream(entry.getWorkspaceModels())
187187
.map(IPluginModelBase::getUnderlyingResource).map(IResource::getProject)
188-
.noneMatch(PluginProject::isJavaProject);
188+
.filter(PluginProject::isJavaProject).collect(Collectors.toList());
189+
boolean addModel = javaProjects.isEmpty();
189190
if (!addModel) {
191+
for (IProject project : javaProjects) {
192+
IPath path = project.getFullPath();
193+
IClasspathEntry projectEntry = JavaCore.newProjectEntry(path);
194+
result.add(projectEntry);
195+
}
190196
continue;
191197
}
192198
IPluginModelBase[] models = entry.getExternalModels();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Simeon Andreev and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Simeon Andreev - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.pde.core.tests.internal.classpath;
15+
16+
import static org.junit.Assert.assertEquals;
17+
import static org.junit.Assert.assertNotNull;
18+
import static org.junit.Assert.assertTrue;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.regex.Matcher;
23+
import java.util.regex.Pattern;
24+
25+
import org.eclipse.core.resources.IProject;
26+
import org.eclipse.core.resources.IWorkspaceRoot;
27+
import org.eclipse.core.resources.ResourcesPlugin;
28+
import org.eclipse.core.runtime.NullProgressMonitor;
29+
import org.eclipse.jdt.core.IJavaElement;
30+
import org.eclipse.jdt.core.IJavaProject;
31+
import org.eclipse.jdt.core.JavaCore;
32+
import org.eclipse.jdt.core.JavaModelException;
33+
import org.eclipse.jdt.core.search.IJavaSearchConstants;
34+
import org.eclipse.jdt.core.search.SearchEngine;
35+
import org.eclipse.jdt.core.search.SearchPattern;
36+
import org.eclipse.jdt.core.search.TypeNameRequestor;
37+
import org.eclipse.jdt.internal.core.JavaModelManager;
38+
import org.eclipse.pde.core.plugin.IPluginModelBase;
39+
import org.eclipse.pde.core.plugin.PluginRegistry;
40+
import org.eclipse.pde.internal.core.SearchablePluginsManager;
41+
import org.eclipse.pde.internal.ui.wizards.imports.PluginImportOperation;
42+
import org.eclipse.pde.internal.ui.wizards.imports.PluginImportWizard;
43+
import org.eclipse.pde.ui.tests.runtime.TestUtils;
44+
import org.eclipse.pde.ui.tests.util.ProjectUtils;
45+
import org.eclipse.ui.IWorkbenchWindow;
46+
import org.eclipse.ui.PlatformUI;
47+
import org.eclipse.ui.handlers.IHandlerService;
48+
import org.junit.ClassRule;
49+
import org.junit.Rule;
50+
import org.junit.Test;
51+
import org.junit.rules.TestName;
52+
import org.junit.rules.TestRule;
53+
54+
/**
55+
* Test that the External Plug-in Libraries project doesn't find duplicated
56+
* types, when a project is imported into the workspace.
57+
*/
58+
public class ExternalJavaSearchClasspathContainerTests {
59+
60+
public static final String ADD_PLUGINS_TO_SEARCH_COMMAND_ID = "org.eclipse.pde.ui.addAllPluginsToJavaSearch";
61+
@ClassRule
62+
public static final TestRule CLEAR_WORKSPACE = ProjectUtils.DELETE_ALL_WORKSPACE_PROJECTS_BEFORE_AND_AFTER;
63+
@Rule
64+
public final TestRule deleteCreatedTestProjectsAfter = ProjectUtils.DELETE_CREATED_WORKSPACE_PROJECTS_AFTER;
65+
@Rule
66+
public final TestName name = new TestName();
67+
68+
@Test
69+
public void testSearchWithImportedProject() throws Exception {
70+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
71+
IHandlerService handlerService = window.getService(IHandlerService.class);
72+
handlerService.executeCommand(ADD_PLUGINS_TO_SEARCH_COMMAND_ID, null);
73+
TestUtils.waitForJobs(name.getMethodName(), 100, 10000);
74+
JavaModelManager.getIndexManager().waitForIndex(false, null);
75+
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
76+
IProject proxyProject = root.getProject(SearchablePluginsManager.PROXY_PROJECT_NAME);
77+
assertNotNull("Adding " + SearchablePluginsManager.PROXY_PROJECT_NAME + " failed", proxyProject);
78+
IJavaProject javaProject = JavaCore.create(proxyProject);
79+
80+
String pluginId = "org.eclipse.core.expressions";
81+
String fqn = "org.eclipse.core.expressions.AndExpression";
82+
// expect a match like this:
83+
// .../plugins/org.eclipse.equinox.common_3.20.300.v20251111-0312.jar|org/eclipse/core/runtime/IProgressMonitor.class
84+
String expected = ".*.jar\\|" + fqn.replace('.', '/') + ".class";
85+
86+
List<String> matches = performSearch(javaProject, fqn);
87+
assertSingleMatch(expected, matches);
88+
89+
IPluginModelBase plugin = PluginRegistry.findModel(pluginId);
90+
IPluginModelBase[] plugins = { plugin };
91+
// import as binary so we don't have to compile, compiling will likely fail
92+
PluginImportWizard.doImportOperation(PluginImportOperation.IMPORT_BINARY, plugins, true, false, null, null);
93+
TestUtils.waitForJobs(name.getMethodName(), 100, 10000);
94+
JavaModelManager.getIndexManager().waitForIndex(false, null);
95+
IProject pluginProject = root.getProject(pluginId);
96+
assertNotNull("Importing " + pluginId + " failed", pluginProject);
97+
98+
// expect a match like this:
99+
// /org.eclipse.core.expressions/org.eclipse.core.expressions_3.9.500.v20250608-0434.jar|org/eclipse/core/expressions/AndExpression.class
100+
expected = pluginProject.getFullPath() + ".*.jar\\|" + fqn.replace('.', '/') + ".class";
101+
matches = performSearch(javaProject, fqn);
102+
assertSingleMatch(expected, matches);
103+
}
104+
105+
private static void assertSingleMatch(String regexp, List<String> matches) {
106+
assertEquals("Expected only one search match, but found: " + matches, 1, matches.size());
107+
Pattern pattern = Pattern.compile(regexp);
108+
Matcher matcher = pattern.matcher(matches.get(0));
109+
assertTrue("Unexpected search matches: " + matches + ", should match regexp: " + regexp, matcher.matches());
110+
}
111+
112+
private static List<String> performSearch(IJavaProject javaProject, String fqn) throws JavaModelException {
113+
TestRequestor requestor = new TestRequestor();
114+
SearchEngine searchEngine = new SearchEngine();
115+
searchEngine.searchAllTypeNames(
116+
fqn.substring(0, fqn.lastIndexOf('.')).toCharArray(),
117+
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
118+
fqn.substring(fqn.lastIndexOf('.') + 1).toCharArray(),
119+
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
120+
IJavaSearchConstants.TYPE,
121+
SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject }),
122+
requestor,
123+
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
124+
new NullProgressMonitor());
125+
List<String> matches = requestor.matches;
126+
return matches;
127+
}
128+
129+
private static class TestRequestor extends TypeNameRequestor {
130+
131+
private final List<String> matches = new ArrayList<>();
132+
133+
@Override
134+
public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) {
135+
matches.add(path);
136+
}
137+
}
138+
}

ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/AllPDETests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.eclipse.pde.core.tests.internal.classpath.ChainedReexportPerformanceTest;
1818
import org.eclipse.pde.core.tests.internal.classpath.ClasspathResolutionTest;
1919
import org.eclipse.pde.core.tests.internal.classpath.ClasspathResolutionTest2;
20+
import org.eclipse.pde.core.tests.internal.classpath.ExternalJavaSearchClasspathContainerTests;
2021
import org.eclipse.pde.core.tests.internal.classpath.RequiredPluginsClasspathContainerPerformanceTest;
2122
import org.eclipse.pde.core.tests.internal.core.builders.BundleErrorReporterTest;
2223
import org.eclipse.pde.core.tests.internal.util.PDESchemaHelperTest;
@@ -69,6 +70,7 @@
6970
DynamicPluginProjectReferencesTest.class, //
7071
ClasspathResolutionTest.class, //
7172
ClasspathResolutionTest2.class, //
73+
ExternalJavaSearchClasspathContainerTests.class, //
7274
BundleErrorReporterTest.class, //
7375
AllPDECoreTests.class, //
7476
ProjectSmartImportTest.class, //

0 commit comments

Comments
 (0)