Skip to content

Commit 2d76237

Browse files
committed
Use PdeProjectAnalyzer to compute required packages
Currently the GatherUnusedDependenciesOperation uses JDT reference search to determine if a package is used. This is not only very slow but also unreliable as it does not take into account indirect references e.g. from superclasses or method signatures. This now uses the PdeProjectAnalyzer to compute all required packages once and then check if an import is providing such package.
1 parent 7ac2ba1 commit 2d76237

1 file changed

Lines changed: 105 additions & 172 deletions

File tree

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/dependencies/GatherUnusedDependenciesOperation.java

Lines changed: 105 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2021 IBM Corporation and others.
2+
* Copyright (c) 2005, 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
@@ -11,56 +11,53 @@
1111
* Contributors:
1212
* IBM Corporation - initial API and implementation
1313
* Johannes Ahlers <Johannes.Ahlers@gmx.de> - bug 477677
14+
* Christoph Läubrich - Use bnd analyzer to compute required packages
1415
*******************************************************************************/
1516
package org.eclipse.pde.internal.ui.search.dependencies;
1617

1718
import java.lang.reflect.InvocationTargetException;
1819
import java.util.ArrayDeque;
1920
import java.util.ArrayList;
21+
import java.util.Arrays;
2022
import java.util.Collection;
2123
import java.util.HashMap;
2224
import java.util.Iterator;
2325
import java.util.List;
2426
import java.util.ListIterator;
27+
import java.util.Map;
28+
import java.util.Set;
29+
import java.util.stream.Collectors;
2530

26-
import org.eclipse.core.resources.IProject;
2731
import org.eclipse.core.runtime.CoreException;
2832
import org.eclipse.core.runtime.IProgressMonitor;
2933
import org.eclipse.core.runtime.SubMonitor;
30-
import org.eclipse.jdt.core.ICompilationUnit;
31-
import org.eclipse.jdt.core.IJavaElement;
32-
import org.eclipse.jdt.core.IJavaProject;
33-
import org.eclipse.jdt.core.IOrdinaryClassFile;
34-
import org.eclipse.jdt.core.IPackageFragment;
35-
import org.eclipse.jdt.core.IType;
36-
import org.eclipse.jdt.core.JavaCore;
37-
import org.eclipse.jdt.core.JavaModelException;
38-
import org.eclipse.jdt.core.search.IJavaSearchConstants;
39-
import org.eclipse.jdt.core.search.IJavaSearchScope;
40-
import org.eclipse.jdt.core.search.SearchEngine;
4134
import org.eclipse.jdt.core.search.SearchMatch;
42-
import org.eclipse.jdt.core.search.SearchParticipant;
43-
import org.eclipse.jdt.core.search.SearchPattern;
4435
import org.eclipse.jdt.core.search.SearchRequestor;
4536
import org.eclipse.jface.operation.IRunnableWithProgress;
4637
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
38+
import org.eclipse.osgi.service.resolver.BundleDescription;
39+
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
4740
import org.eclipse.pde.core.plugin.IPluginImport;
4841
import org.eclipse.pde.core.plugin.IPluginModelBase;
4942
import org.eclipse.pde.core.plugin.PluginRegistry;
5043
import org.eclipse.pde.internal.core.ClasspathUtilCore;
44+
import org.eclipse.pde.internal.core.bnd.PdeProjectAnalyzer;
5145
import org.eclipse.pde.internal.core.ibundle.IBundle;
46+
import org.eclipse.pde.internal.core.ibundle.IBundleModel;
5247
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
5348
import org.eclipse.pde.internal.core.ibundle.IManifestHeader;
5449
import org.eclipse.pde.internal.core.plugin.PluginImport;
5550
import org.eclipse.pde.internal.core.search.PluginJavaSearchUtil;
5651
import org.eclipse.pde.internal.core.text.bundle.ExportPackageHeader;
5752
import org.eclipse.pde.internal.core.text.bundle.ImportPackageHeader;
5853
import org.eclipse.pde.internal.core.text.bundle.ImportPackageObject;
59-
import org.eclipse.pde.internal.ui.PDEPlugin;
6054
import org.eclipse.pde.internal.ui.PDEUIMessages;
6155
import org.eclipse.pde.internal.ui.util.TextUtil;
6256
import org.osgi.framework.Constants;
6357

58+
import aQute.bnd.osgi.Descriptors.PackageRef;
59+
import aQute.bnd.osgi.Packages;
60+
6461
public class GatherUnusedDependenciesOperation implements IRunnableWithProgress {
6562

6663
static class Requestor extends SearchRequestor {
@@ -77,37 +74,43 @@ public boolean foundMatches() {
7774
}
7875

7976
private final IPluginModelBase fModel;
80-
private ArrayList<Object> fList;
77+
private List<Object> fList;
8178

8279
public GatherUnusedDependenciesOperation(IPluginModelBase model) {
8380
fModel = model;
8481
}
8582

8683
@Override
8784
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
88-
89-
ImportPackageObject[] packages = null;
90-
Collection<String> exportedPackages = null;
91-
if (ClasspathUtilCore.hasBundleStructure(fModel)) {
92-
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
93-
IManifestHeader header = bundle.getManifestHeader(Constants.IMPORT_PACKAGE);
94-
if (header instanceof ImportPackageHeader) {
95-
packages = ((ImportPackageHeader) header).getPackages();
96-
} else if (header != null && header.getValue() != null) {
97-
header = new ImportPackageHeader(Constants.IMPORT_PACKAGE, header.getValue(), bundle,
98-
TextUtil.getDefaultLineDelimiter());
99-
packages = ((ImportPackageHeader) header).getPackages();
100-
}
101-
102-
header = bundle.getManifestHeader(Constants.EXPORT_PACKAGE);
103-
if (header instanceof ExportPackageHeader) {
104-
exportedPackages = ((ExportPackageHeader) header).getPackageNames();
105-
} else if (header != null && header.getValue() != null) {
106-
header = new ExportPackageHeader(Constants.EXPORT_PACKAGE, header.getValue(), bundle,
107-
TextUtil.getDefaultLineDelimiter());
108-
exportedPackages = ((ExportPackageHeader) header).getPackageNames();
85+
if (!ClasspathUtilCore.hasBundleStructure(fModel)) {
86+
return;
87+
}
88+
Set<String> computedPackages;
89+
try (PdeProjectAnalyzer analyzer = new PdeProjectAnalyzer(fModel.getUnderlyingResource().getProject(), false)) {
90+
analyzer.setImportPackage("*"); //$NON-NLS-1$
91+
analyzer.calcManifest();
92+
Packages imports = analyzer.getImports();
93+
if (imports == null) {
94+
computedPackages = Set.of();
95+
} else {
96+
computedPackages = imports.keySet().stream().map(PackageRef::getFQN).collect(Collectors.toSet());
10997
}
98+
} catch (InterruptedException e) {
99+
throw e;
100+
} catch (Exception e) {
101+
throw new InvocationTargetException(e);
110102
}
103+
ImportPackageObject[] packages = null;
104+
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
105+
IManifestHeader header = bundle.getManifestHeader(Constants.IMPORT_PACKAGE);
106+
if (header instanceof ImportPackageHeader) {
107+
packages = ((ImportPackageHeader) header).getPackages();
108+
} else if (header != null && header.getValue() != null) {
109+
header = new ImportPackageHeader(Constants.IMPORT_PACKAGE, header.getValue(), bundle,
110+
TextUtil.getDefaultLineDelimiter());
111+
packages = ((ImportPackageHeader) header).getPackages();
112+
}
113+
Collection<String> exportedPackages = getExportedPackages(fModel);
111114
IPluginImport[] imports = fModel.getPluginBase().getImports();
112115

113116
int totalWork = imports.length * 3 + (packages != null ? packages.length : 0) + 1;
@@ -117,58 +120,83 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
117120
fList = new ArrayList<>();
118121
for (IPluginImport pluginImport : imports) {
119122
if (subMonitor.isCanceled()) {
120-
break;
123+
return;
121124
}
122-
if (isUnused(pluginImport, subMonitor.split(3))) {
125+
if (isUnused(pluginImport, computedPackages, subMonitor.split(3))) {
123126
fList.add(pluginImport);
124127
} else {
125128
usedPlugins.put(pluginImport.getId(), pluginImport);
126129
}
127130
updateMonitor(subMonitor, fList.size());
128131
}
129132

130-
ArrayList<ImportPackageObject> usedPackages = new ArrayList<>();
133+
List<ImportPackageObject> usedPackages = new ArrayList<>();
131134
if (packages != null && !subMonitor.isCanceled()) {
132135
for (ImportPackageObject importPackage : packages) {
133136
if (subMonitor.isCanceled()) {
134-
break;
137+
return;
135138
}
136-
if (isUnused(importPackage, exportedPackages, subMonitor.split(1))) {
139+
if (isUnused(importPackage, exportedPackages, computedPackages, subMonitor.split(1))) {
137140
fList.add(importPackage);
138141
updateMonitor(subMonitor, fList.size());
139142
} else {
140143
usedPackages.add(importPackage);
141144
}
142145
}
143146
}
144-
if (!subMonitor.isCanceled()) {
145-
minimizeDependencies(usedPlugins, usedPackages, subMonitor);
147+
if (subMonitor.isCanceled()) {
148+
return;
149+
}
150+
minimizeDependencies(usedPlugins, usedPackages, subMonitor);
151+
removeBuddies();
152+
removeReexported();
153+
}
146154

155+
private static Collection<String> getExportedPackages(IPluginModelBase model) {
156+
if (model instanceof IBundlePluginModelBase plugin) {
157+
IBundleModel bundleModel = plugin.getBundleModel();
158+
if (bundleModel != null) {
159+
IBundle bundle = bundleModel.getBundle();
160+
IManifestHeader header = bundle.getManifestHeader(Constants.EXPORT_PACKAGE);
161+
if (header instanceof ExportPackageHeader pkg) {
162+
return pkg.getPackageNames();
163+
} else if (header != null && header.getValue() != null) {
164+
ExportPackageHeader pkg = new ExportPackageHeader(Constants.EXPORT_PACKAGE, header.getValue(),
165+
bundle, TextUtil.getDefaultLineDelimiter());
166+
return pkg.getPackageNames();
167+
}
168+
}
147169
}
148-
if (ClasspathUtilCore.hasBundleStructure(fModel)) {
149-
removeBuddies();
150-
removeReexported();
170+
BundleDescription bundleDescription = model.getBundleDescription();
171+
if (bundleDescription != null) {
172+
return Arrays.stream(bundleDescription.getExportPackages()).map(ExportPackageDescription::getName).toList();
151173
}
174+
return List.of();
152175
}
153176

154177
protected void removeBuddies() {
155-
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
156-
IManifestHeader header = bundle.getManifestHeader(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER);
157-
if (header != null) {
158-
String values = header.getValue();
159-
String[] registerBud = values.split("\\s*,\\s*"); //$NON-NLS-1$
160-
List<Object> found = new ArrayList<>();
161-
for (String string : registerBud) {
162-
for (Object obj : fList) {
163-
if (obj instanceof PluginImport) {
164-
String id = ((PluginImport) obj).getId();
165-
if (string.equals(id)) {
166-
found.add(obj);
178+
if (fModel instanceof IBundlePluginModelBase plugin) {
179+
IBundleModel bundleModel = plugin.getBundleModel();
180+
if (bundleModel != null) {
181+
IManifestHeader header = bundleModel.getBundle()
182+
.getManifestHeader(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER);
183+
if (header != null) {
184+
String values = header.getValue();
185+
String[] registerBud = values.split("\\s*,\\s*"); //$NON-NLS-1$
186+
List<Object> found = new ArrayList<>();
187+
for (String string : registerBud) {
188+
for (Object obj : fList) {
189+
if (obj instanceof PluginImport) {
190+
String id = ((PluginImport) obj).getId();
191+
if (string.equals(id)) {
192+
found.add(obj);
193+
}
194+
}
167195
}
168196
}
197+
fList.removeAll(found);
169198
}
170199
}
171-
fList.removeAll(found);
172200
}
173201
}
174202

@@ -191,125 +219,29 @@ private void updateMonitor(IProgressMonitor monitor, int size) {
191219
+ PDEUIMessages.DependencyExtent_found);
192220
}
193221

194-
private boolean isUnused(IPluginImport plugin, IProgressMonitor monitor) {
222+
private boolean isUnused(IPluginImport plugin, Set<String> computedPackages, IProgressMonitor monitor) {
195223
IPluginModelBase[] models = PluginJavaSearchUtil.getPluginImports(plugin);
196-
return !provideJavaClasses(models, monitor);
197-
}
198-
199-
private boolean isUnused(ImportPackageObject pkg, Collection<String> exportedPackages, IProgressMonitor monitor) {
200-
if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) {
201-
return false;
202-
}
203-
return !provideJavaClasses(pkg, monitor);
204-
}
205-
206-
private boolean provideJavaClasses(IPluginModelBase[] models, IProgressMonitor monitor) {
207-
try {
208-
IProject project = fModel.getUnderlyingResource().getProject();
209-
if (!project.hasNature(JavaCore.NATURE_ID)) {
210-
return false;
211-
}
212-
213-
IJavaProject jProject = JavaCore.create(project);
214-
IPackageFragment[] packageFragments = PluginJavaSearchUtil.collectPackageFragments(models, jProject, true);
215-
SearchEngine engine = new SearchEngine();
216-
IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject);
217-
218-
SubMonitor subMonitor = SubMonitor.convert(monitor, packageFragments.length * 2);
219-
for (IPackageFragment pkgFragment : packageFragments) {
220-
SubMonitor iterationMonitor = subMonitor.split(2);
221-
if (pkgFragment.hasChildren()) {
222-
Requestor requestor = new Requestor();
223-
SearchPattern pattern = SearchPattern.createPattern(pkgFragment, IJavaSearchConstants.REFERENCES);
224-
if (pattern == null) {
225-
continue;
226-
}
227-
engine.search(pattern,
228-
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope,
229-
requestor, iterationMonitor.split(1));
230-
if (requestor.foundMatches()) {
231-
if (provideJavaClasses(pkgFragment, engine, searchScope,
232-
iterationMonitor.split(1))) {
233-
return true;
234-
}
235-
}
236-
}
237-
}
238-
} catch (CoreException e) {
239-
PDEPlugin.logException(e);
240-
}
241-
return false;
242-
}
243-
244-
private boolean provideJavaClasses(IPackageFragment packageFragment, SearchEngine engine, IJavaSearchScope searchScope, IProgressMonitor monitor) throws JavaModelException, CoreException {
245-
Requestor requestor;
246-
IJavaElement[] children = packageFragment.getChildren();
247-
SubMonitor subMonitor = SubMonitor.convert(monitor, children.length);
248-
249-
for (IJavaElement child : children) {
250-
IType[] types = null;
251-
if (child instanceof ICompilationUnit) {
252-
types = ((ICompilationUnit) child).getAllTypes();
253-
} else if (child instanceof IOrdinaryClassFile) {
254-
types = new IType[] { ((IOrdinaryClassFile) child).getType() };
255-
}
256-
if (types != null) {
257-
SubMonitor iterationMonitor = subMonitor.split(1).setWorkRemaining(types.length);
258-
for (IType type : types) {
259-
requestor = new Requestor();
260-
SearchPattern pattern = SearchPattern.createPattern(type, IJavaSearchConstants.REFERENCES);
261-
if (pattern == null) {
262-
continue;
263-
}
264-
engine.search(pattern,
265-
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope,
266-
requestor, iterationMonitor.split(1));
267-
if (requestor.foundMatches()) {
268-
return true;
269-
}
224+
for (IPluginModelBase model : models) {
225+
Collection<String> exportedPackages = getExportedPackages(model);
226+
for (String exportedPackage : exportedPackages) {
227+
if (computedPackages.contains(exportedPackage)) {
228+
return false;
270229
}
271-
} else {
272-
subMonitor.worked(1);
273230
}
274231
}
275-
return false;
232+
return true;
276233
}
277234

278-
private boolean provideJavaClasses(ImportPackageObject pkg, IProgressMonitor monitor) {
279-
try {
280-
IProject project = fModel.getUnderlyingResource().getProject();
281-
282-
if (!project.hasNature(JavaCore.NATURE_ID)) {
283-
return false;
284-
}
285-
286-
SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
287-
IJavaProject jProject = JavaCore.create(project);
288-
SearchEngine engine = new SearchEngine();
289-
IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject);
290-
Requestor requestor = new Requestor();
291-
String packageName = pkg.getName();
292-
293-
SearchPattern pattern = SearchPattern.createPattern(packageName, IJavaSearchConstants.PACKAGE,
294-
IJavaSearchConstants.REFERENCES, SearchPattern.R_EXACT_MATCH);
295-
if (pattern == null) {
296-
return false;
297-
}
298-
engine.search(
299-
pattern,
300-
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, requestor,
301-
subMonitor.split(1));
302-
303-
if (requestor.foundMatches()) {
304-
return true;
305-
}
306-
} catch (CoreException e) {
307-
PDEPlugin.logException(e);
235+
private boolean isUnused(ImportPackageObject pkg, Collection<String> exportedPackages, Set<String> computedPackages,
236+
IProgressMonitor monitor) {
237+
if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) {
238+
return false;
308239
}
309-
return false;
240+
String name = pkg.getName();
241+
return !computedPackages.contains(name);
310242
}
311243

312-
public ArrayList<Object> getList() {
244+
public List<Object> getList() {
313245
return fList;
314246
}
315247

@@ -330,7 +262,8 @@ public static void removeDependencies(IPluginModelBase model, Object[] elements)
330262
}
331263
}
332264

333-
private void minimizeDependencies(HashMap<String, IPluginImport> usedPlugins, ArrayList<ImportPackageObject> usedPackages, IProgressMonitor monitor) {
265+
private void minimizeDependencies(Map<String, IPluginImport> usedPlugins, List<ImportPackageObject> usedPackages,
266+
IProgressMonitor monitor) {
334267
ListIterator<ImportPackageObject> li = usedPackages.listIterator();
335268
while (li.hasNext()) {
336269
ImportPackageObject ipo = li.next();

0 commit comments

Comments
 (0)