Skip to content

Commit 2b705a2

Browse files
committed
Streamline compile error resolution proposals and strengthen typing
1 parent 7e6153c commit 2b705a2

File tree

4 files changed

+163
-261
lines changed

4 files changed

+163
-261
lines changed

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/correction/java/FindClassResolutionsOperation.java

Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2008, 2019 IBM Corporation and others.
2+
* Copyright (c) 2008, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,14 +13,15 @@
1313
*******************************************************************************/
1414
package org.eclipse.pde.internal.ui.correction.java;
1515

16+
import java.util.Arrays;
1617
import java.util.Collections;
1718
import java.util.HashMap;
1819
import java.util.HashSet;
19-
import java.util.Iterator;
2020
import java.util.LinkedHashSet;
21+
import java.util.List;
2122
import java.util.Map;
22-
import java.util.Map.Entry;
2323
import java.util.Set;
24+
import java.util.stream.Collectors;
2425

2526
import org.eclipse.core.resources.IProject;
2627
import org.eclipse.core.resources.IResource;
@@ -41,11 +42,13 @@
4142
import org.eclipse.jdt.core.search.SearchParticipant;
4243
import org.eclipse.jdt.core.search.SearchPattern;
4344
import org.eclipse.jdt.core.search.SearchRequestor;
45+
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
4446
import org.eclipse.jface.operation.IRunnableWithProgress;
4547
import org.eclipse.osgi.service.resolver.BundleDescription;
4648
import org.eclipse.osgi.service.resolver.BundleSpecification;
4749
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
4850
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
51+
import org.eclipse.osgi.service.resolver.State;
4952
import org.eclipse.osgi.service.resolver.StateHelper;
5053
import org.eclipse.pde.core.plugin.IPluginModelBase;
5154
import org.eclipse.pde.core.plugin.PluginRegistry;
@@ -59,9 +62,9 @@
5962
*/
6063
public class FindClassResolutionsOperation implements IRunnableWithProgress {
6164

62-
String fClassName = null;
63-
IProject fProject = null;
64-
AbstractClassResolutionCollector fCollector = null;
65+
private String fClassName = null;
66+
private IProject fProject = null;
67+
private AbstractClassResolutionCollector fCollector = null;
6568
private CompilationUnit fCompilationUnit;
6669

6770
/**
@@ -78,7 +81,9 @@ public static abstract class AbstractClassResolutionCollector {
7881
* Import-Package. The proposals can be created with the help of the
7982
* JavaResolutionFactory
8083
*/
81-
abstract public void addResolutionModification(IProject project, ExportPackageDescription desc);
84+
public void addResolutionModification(IProject project, ExportPackageDescription desc) {
85+
addResolutionModification(project, desc, null, ""); //$NON-NLS-1$
86+
}
8287

8388
/**
8489
* This method is meant to be sub-classed. The subclass should decide if
@@ -93,11 +98,15 @@ abstract public void addResolutionModification(IProject project, ExportPackageDe
9398
* Adds an export package proposal. Subclasses should implement the
9499
* actual adding to the collection.
95100
*/
96-
public Object addExportPackageResolutionModification(IPackageFragment aPackage) {
101+
public IJavaCompletionProposal addExportPackageResolutionModification(IPackageFragment aPackage) {
97102
if (aPackage.exists()) {
103+
// TODO: try (and check null inbetween)
104+
// aPackage.getJavaProject().getProject();
98105
IResource packageResource = aPackage.getResource();
99106
if (packageResource != null) {
100-
return JavaResolutionFactory.createExportPackageProposal(packageResource.getProject(), aPackage, JavaResolutionFactory.TYPE_JAVA_COMPLETION, 100);
107+
IProject project = packageResource.getProject();
108+
var change = JavaResolutionFactory.createExportPackageChange(project, aPackage);
109+
return JavaResolutionFactory.createJavaCompletionProposal(change, 100);
101110
}
102111
}
103112
return null;
@@ -114,13 +123,16 @@ public Object addRequireBundleModification(IProject project, ExportPackageDescri
114123
* Adds a require bundle proposal. Subclasses should implement the
115124
* actual adding to the collection.
116125
*/
117-
public Object addRequireBundleModification(IProject project, ExportPackageDescription desc, int relevance,
118-
CompilationUnit cu, String qualifiedTypeToImport) {
119-
return JavaResolutionFactory.createRequireBundleProposal(project, desc,
120-
JavaResolutionFactory.TYPE_JAVA_COMPLETION, relevance, cu, qualifiedTypeToImport);
126+
public IJavaCompletionProposal addRequireBundleModification(IProject project, ExportPackageDescription desc,
127+
int relevance, CompilationUnit cu, String qualifiedTypeToImport) {
128+
BundleDescription exporter = desc.getExporter();
129+
if (exporter == null) {
130+
return null;
131+
}
132+
var change = JavaResolutionFactory.createRequireBundleChange(project, exporter, cu, qualifiedTypeToImport);
133+
return JavaResolutionFactory.createJavaCompletionProposal(change, relevance);
121134
}
122135

123-
124136
/**
125137
* Adds a search repositories proposal. Subclasses should implement the actual adding to the collection.
126138
*/
@@ -201,38 +213,31 @@ public void run(IProgressMonitor monitor) {
201213
}
202214
return;
203215
}
204-
205-
Iterator<Entry<String, ExportPackageDescription>> validPackagesIter = validPackages.entrySet().iterator();
206-
Set<ExportPackageDescription> visiblePkgs = null;
216+
// getting visible packages is not very efficient -> do it only once
217+
Set<ExportPackageDescription> visiblePackages = !validPackages.isEmpty() ? getVisiblePackages() : null;
207218
boolean allowMultipleFixes = packageName == null;
208-
while (validPackagesIter.hasNext() && (allowMultipleFixes || !fCollector.isDone())) {
209-
// since getting visible packages is not very efficient, only do it once and cache result
210-
if (visiblePkgs == null) {
211-
visiblePkgs = getVisiblePackages();
212-
}
213-
Entry<String, ExportPackageDescription> currentEntry = validPackagesIter.next();
214-
ExportPackageDescription currentPackage = currentEntry.getValue();
219+
for (var validPackagesIter = validPackages.entrySet().iterator(); validPackagesIter.hasNext()
220+
&& (allowMultipleFixes || !fCollector.isDone());) {
221+
var entry = validPackagesIter.next();
222+
String qualifiedType = entry.getKey();
223+
ExportPackageDescription currentPackage = entry.getValue();
215224
// if package is already visible, skip over
216-
if (visiblePkgs.contains(currentPackage)) {
225+
if (visiblePackages.contains(currentPackage)) {
217226
continue;
218227
}
219228
// if currentPackage will resolve class and is valid, pass it to collector
220-
fCollector.addResolutionModification(fProject, currentPackage, fCompilationUnit, currentEntry.getKey());
229+
fCollector.addResolutionModification(fProject, currentPackage, fCompilationUnit, qualifiedType);
221230
}
222-
223231
// additionally add require bundle proposals
224232
Set<String> bundleNames = getCurrentBundleNames();
225-
for (validPackagesIter = validPackages.entrySet().iterator(); validPackagesIter.hasNext();) {
226-
Entry<String, ExportPackageDescription> currentEntry = validPackagesIter.next();
227-
ExportPackageDescription currentPackage = currentEntry.getValue();
233+
validPackages.forEach((key, currentPackage) -> {
228234
BundleDescription desc = currentPackage.getExporter();
229235
// Ignore already required bundles and duplicate proposals (currently we do not consider version constraints)
230236
if (desc != null && !bundleNames.contains(desc.getName())) {
231-
fCollector.addRequireBundleModification(fProject, currentPackage, 3, fCompilationUnit,
232-
currentEntry.getKey());
237+
fCollector.addRequireBundleModification(fProject, currentPackage, 3, fCompilationUnit, key);
233238
bundleNames.add(desc.getName());
234239
}
235-
}
240+
});
236241
}
237242
}
238243

@@ -251,7 +256,8 @@ private Map<String, ExportPackageDescription> getValidPackages(String typeName,
251256
if (importPkgs != null) {
252257
if (packageName != null) {
253258
if (!isImportedPackage(packageName, importPkgs)) {
254-
validPackages = getValidPackages(packageName, qualifiedTypeToImport);
259+
List<ExportPackageDescription> packages = getValidPackages(packageName);
260+
validPackages = !packages.isEmpty() ? Map.of(qualifiedTypeToImport, packages.getLast()) : Map.of();
255261
}
256262
subMonitor.split(1);
257263
} else {
@@ -279,18 +285,10 @@ private Map<String, ExportPackageDescription> findValidPackagesContainingSimpleT
279285
ImportPackageSpecification[] importPkgs, Set<IPackageFragment> packagesToExport, IProgressMonitor monitor) {
280286
SubMonitor subMonitor = SubMonitor.convert(monitor);
281287

282-
IPluginModelBase[] activeModels = PluginRegistry.getActiveModels();
283-
Set<IJavaProject> javaProjects = new LinkedHashSet<>(activeModels.length * 2);
288+
Set<IJavaProject> javaProjects = Arrays.stream(PluginRegistry.getWorkspaceModels())
289+
.map(IPluginModelBase::getUnderlyingResource).map(IResource::getProject).map(JavaCore::create)
290+
.filter(IJavaProject::exists).collect(Collectors.toCollection(LinkedHashSet::new));
284291

285-
for (IPluginModelBase model : activeModels) {
286-
IResource resource = model.getUnderlyingResource();
287-
if (resource != null && resource.isAccessible()) {
288-
IJavaProject javaProject = JavaCore.create(resource.getProject());
289-
if (javaProject.exists()) {
290-
javaProjects.add(javaProject);
291-
}
292-
}
293-
}
294292
final IJavaProject currentJavaProject = JavaCore.create(fProject);
295293
javaProjects.remove(currentJavaProject); // no need to search in current project itself
296294

@@ -328,10 +326,10 @@ public void acceptSearchMatch(SearchMatch aMatch) throws CoreException {
328326

329327
if (!packages.isEmpty()) {
330328
// transform to ExportPackageDescriptions
331-
Map<String, ExportPackageDescription> exportDescriptions = new HashMap<>(packages.size());
329+
State state = PDECore.getDefault().getModelManager().getState().getState();
332330

333331
// remove system packages if they happen to be included. Adding a system package won't resolve anything, since package package already comes from JRE
334-
ExportPackageDescription[] systemPackages = PDECore.getDefault().getModelManager().getState().getState().getSystemPackages();
332+
ExportPackageDescription[] systemPackages = state.getSystemPackages();
335333
for (ExportPackageDescription systemPackage : systemPackages) {
336334
packages.remove(systemPackage.getName());
337335
}
@@ -341,7 +339,8 @@ public void acceptSearchMatch(SearchMatch aMatch) throws CoreException {
341339
}
342340

343341
// finally create the list of ExportPackageDescriptions
344-
ExportPackageDescription[] knownPackages = PDECore.getDefault().getModelManager().getState().getState().getExportedPackages();
342+
Map<String, ExportPackageDescription> exportDescriptions = new HashMap<>(packages.size());
343+
ExportPackageDescription[] knownPackages = state.getExportedPackages();
345344
for (ExportPackageDescription knownPackage : knownPackages) {
346345
if (packages.containsKey(knownPackage.getName())) {
347346
exportDescriptions.put(qualifiedTypeNames.get(knownPackage.getName()), knownPackage);
@@ -371,39 +370,26 @@ private boolean isImportedPackage(String packageName, ImportPackageSpecification
371370
return false;
372371
}
373372

374-
private static Map<String, ExportPackageDescription> getValidPackages(String pkgName, String qualifiedTypeToImport) {
375-
ExportPackageDescription[] knownPackages = PDECore.getDefault().getModelManager().getState().getState().getExportedPackages();
376-
Map<String, ExportPackageDescription> validPackages = new HashMap<>();
377-
for (ExportPackageDescription knownPackage : knownPackages) {
378-
if (knownPackage.getName().equals(pkgName)) {
379-
validPackages.put(knownPackage.getName(), knownPackage);
380-
}
381-
}
373+
private static List<ExportPackageDescription> getValidPackages(String packageName) {
374+
State state = PDECore.getDefault().getModelManager().getState().getState();
375+
List<ExportPackageDescription> validPackages = Arrays.stream(state.getExportedPackages())
376+
.filter(p -> packageName.equals(p.getName())).toList();
382377
// remove system packages if they happen to be included. Adding a system package won't resolve anything, since package package already comes from JRE
383378
if (!validPackages.isEmpty()) {
384-
knownPackages = PDECore.getDefault().getModelManager().getState().getState().getSystemPackages();
385-
for (ExportPackageDescription knownPackage : knownPackages) {
386-
validPackages.remove(knownPackage.getName());
379+
ExportPackageDescription[] systemPackages = state.getSystemPackages();
380+
if (Arrays.stream(systemPackages).map(ExportPackageDescription::getName).anyMatch(packageName::equals)) {
381+
return List.of();
387382
}
388383
}
389-
Map<String, ExportPackageDescription> packages = new HashMap<>();
390-
for (ExportPackageDescription exportPackageDescription : validPackages.values()) {
391-
packages.put(qualifiedTypeToImport, exportPackageDescription);
392-
}
393-
return packages;
384+
return validPackages;
394385
}
395386

396387
private Set<ExportPackageDescription> getVisiblePackages() {
397388
IPluginModelBase base = PluginRegistry.findModel(fProject);
398389
if (base != null) {
399390
BundleDescription desc = base.getBundleDescription();
400-
401391
StateHelper helper = BundleHelper.getPlatformAdmin().getStateHelper();
402-
ExportPackageDescription[] visiblePkgs = helper.getVisiblePackages(desc);
403-
404-
HashSet<ExportPackageDescription> set = new HashSet<>();
405-
Collections.addAll(set, visiblePkgs);
406-
return set;
392+
return Set.of(helper.getVisiblePackages(desc));
407393
}
408394
return Collections.emptySet();
409395
}

0 commit comments

Comments
 (0)