Skip to content

Commit c775927

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

File tree

4 files changed

+161
-264
lines changed

4 files changed

+161
-264
lines changed

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

Lines changed: 54 additions & 73 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,13 @@ 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()) {
98103
IResource packageResource = aPackage.getResource();
99104
if (packageResource != null) {
100-
return JavaResolutionFactory.createExportPackageProposal(packageResource.getProject(), aPackage, JavaResolutionFactory.TYPE_JAVA_COMPLETION, 100);
105+
IProject project = packageResource.getProject();
106+
var change = JavaResolutionFactory.createExportPackageChange(project, aPackage);
107+
return JavaResolutionFactory.createJavaCompletionProposal(change, 100);
101108
}
102109
}
103110
return null;
@@ -114,13 +121,16 @@ public Object addRequireBundleModification(IProject project, ExportPackageDescri
114121
* Adds a require bundle proposal. Subclasses should implement the
115122
* actual adding to the collection.
116123
*/
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);
124+
public IJavaCompletionProposal addRequireBundleModification(IProject project, ExportPackageDescription desc,
125+
int relevance, CompilationUnit cu, String qualifiedTypeToImport) {
126+
BundleDescription exporter = desc.getExporter();
127+
if (exporter == null) {
128+
return null;
129+
}
130+
var change = JavaResolutionFactory.createRequireBundleChange(project, exporter, cu, qualifiedTypeToImport);
131+
return JavaResolutionFactory.createJavaCompletionProposal(change, relevance);
121132
}
122133

123-
124134
/**
125135
* Adds a search repositories proposal. Subclasses should implement the actual adding to the collection.
126136
*/
@@ -134,9 +144,6 @@ public Object addSearchRepositoriesModification(String packageName) {
134144
public boolean isDone() {
135145
return false;
136146
}
137-
138-
139-
140147
}
141148

142149
/**
@@ -201,38 +208,31 @@ public void run(IProgressMonitor monitor) {
201208
}
202209
return;
203210
}
204-
205-
Iterator<Entry<String, ExportPackageDescription>> validPackagesIter = validPackages.entrySet().iterator();
206-
Set<ExportPackageDescription> visiblePkgs = null;
211+
// getting visible packages is not very efficient -> do it only once
212+
Set<ExportPackageDescription> visiblePackages = !validPackages.isEmpty() ? getVisiblePackages() : null;
207213
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();
214+
for (var validPackagesIter = validPackages.entrySet().iterator(); validPackagesIter.hasNext()
215+
&& (allowMultipleFixes || !fCollector.isDone());) {
216+
var entry = validPackagesIter.next();
217+
String qualifiedType = entry.getKey();
218+
ExportPackageDescription currentPackage = entry.getValue();
215219
// if package is already visible, skip over
216-
if (visiblePkgs.contains(currentPackage)) {
220+
if (visiblePackages.contains(currentPackage)) {
217221
continue;
218222
}
219223
// if currentPackage will resolve class and is valid, pass it to collector
220-
fCollector.addResolutionModification(fProject, currentPackage, fCompilationUnit, currentEntry.getKey());
224+
fCollector.addResolutionModification(fProject, currentPackage, fCompilationUnit, qualifiedType);
221225
}
222-
223226
// additionally add require bundle proposals
224227
Set<String> bundleNames = getCurrentBundleNames();
225-
for (validPackagesIter = validPackages.entrySet().iterator(); validPackagesIter.hasNext();) {
226-
Entry<String, ExportPackageDescription> currentEntry = validPackagesIter.next();
227-
ExportPackageDescription currentPackage = currentEntry.getValue();
228+
validPackages.forEach((key, currentPackage) -> {
228229
BundleDescription desc = currentPackage.getExporter();
229230
// Ignore already required bundles and duplicate proposals (currently we do not consider version constraints)
230231
if (desc != null && !bundleNames.contains(desc.getName())) {
231-
fCollector.addRequireBundleModification(fProject, currentPackage, 3, fCompilationUnit,
232-
currentEntry.getKey());
232+
fCollector.addRequireBundleModification(fProject, currentPackage, 3, fCompilationUnit, key);
233233
bundleNames.add(desc.getName());
234234
}
235-
}
235+
});
236236
}
237237
}
238238

@@ -251,7 +251,8 @@ private Map<String, ExportPackageDescription> getValidPackages(String typeName,
251251
if (importPkgs != null) {
252252
if (packageName != null) {
253253
if (!isImportedPackage(packageName, importPkgs)) {
254-
validPackages = getValidPackages(packageName, qualifiedTypeToImport);
254+
List<ExportPackageDescription> packages = getValidPackages(packageName);
255+
validPackages = !packages.isEmpty() ? Map.of(qualifiedTypeToImport, packages.getLast()) : Map.of();
255256
}
256257
subMonitor.split(1);
257258
} else {
@@ -279,18 +280,10 @@ private Map<String, ExportPackageDescription> findValidPackagesContainingSimpleT
279280
ImportPackageSpecification[] importPkgs, Set<IPackageFragment> packagesToExport, IProgressMonitor monitor) {
280281
SubMonitor subMonitor = SubMonitor.convert(monitor);
281282

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

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-
}
294287
final IJavaProject currentJavaProject = JavaCore.create(fProject);
295288
javaProjects.remove(currentJavaProject); // no need to search in current project itself
296289

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

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

333326
// 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();
327+
ExportPackageDescription[] systemPackages = state.getSystemPackages();
335328
for (ExportPackageDescription systemPackage : systemPackages) {
336329
packages.remove(systemPackage.getName());
337330
}
@@ -341,7 +334,8 @@ public void acceptSearchMatch(SearchMatch aMatch) throws CoreException {
341334
}
342335

343336
// finally create the list of ExportPackageDescriptions
344-
ExportPackageDescription[] knownPackages = PDECore.getDefault().getModelManager().getState().getState().getExportedPackages();
337+
Map<String, ExportPackageDescription> exportDescriptions = new HashMap<>(packages.size());
338+
ExportPackageDescription[] knownPackages = state.getExportedPackages();
345339
for (ExportPackageDescription knownPackage : knownPackages) {
346340
if (packages.containsKey(knownPackage.getName())) {
347341
exportDescriptions.put(qualifiedTypeNames.get(knownPackage.getName()), knownPackage);
@@ -371,39 +365,26 @@ private boolean isImportedPackage(String packageName, ImportPackageSpecification
371365
return false;
372366
}
373367

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-
}
368+
private static List<ExportPackageDescription> getValidPackages(String packageName) {
369+
State state = PDECore.getDefault().getModelManager().getState().getState();
370+
List<ExportPackageDescription> validPackages = Arrays.stream(state.getExportedPackages())
371+
.filter(p -> packageName.equals(p.getName())).toList();
382372
// remove system packages if they happen to be included. Adding a system package won't resolve anything, since package package already comes from JRE
383373
if (!validPackages.isEmpty()) {
384-
knownPackages = PDECore.getDefault().getModelManager().getState().getState().getSystemPackages();
385-
for (ExportPackageDescription knownPackage : knownPackages) {
386-
validPackages.remove(knownPackage.getName());
374+
ExportPackageDescription[] systemPackages = state.getSystemPackages();
375+
if (Arrays.stream(systemPackages).map(ExportPackageDescription::getName).anyMatch(packageName::equals)) {
376+
return List.of();
387377
}
388378
}
389-
Map<String, ExportPackageDescription> packages = new HashMap<>();
390-
for (ExportPackageDescription exportPackageDescription : validPackages.values()) {
391-
packages.put(qualifiedTypeToImport, exportPackageDescription);
392-
}
393-
return packages;
379+
return validPackages;
394380
}
395381

396382
private Set<ExportPackageDescription> getVisiblePackages() {
397383
IPluginModelBase base = PluginRegistry.findModel(fProject);
398384
if (base != null) {
399385
BundleDescription desc = base.getBundleDescription();
400-
401386
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;
387+
return Set.of(helper.getVisiblePackages(desc));
407388
}
408389
return Collections.emptySet();
409390
}

0 commit comments

Comments
 (0)