Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2021 IBM Corporation and others.
* Copyright (c) 2005, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -11,56 +11,53 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Johannes Ahlers <Johannes.Ahlers@gmx.de> - bug 477677
* Christoph Läubrich - Use bnd analyzer to compute required packages
*******************************************************************************/
package org.eclipse.pde.internal.ui.search.dependencies;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.eclipse.pde.core.plugin.IPluginImport;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.core.ClasspathUtilCore;
import org.eclipse.pde.internal.core.bnd.PdeProjectAnalyzer;
import org.eclipse.pde.internal.core.ibundle.IBundle;
import org.eclipse.pde.internal.core.ibundle.IBundleModel;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
import org.eclipse.pde.internal.core.ibundle.IManifestHeader;
import org.eclipse.pde.internal.core.plugin.PluginImport;
import org.eclipse.pde.internal.core.search.PluginJavaSearchUtil;
import org.eclipse.pde.internal.core.text.bundle.ExportPackageHeader;
import org.eclipse.pde.internal.core.text.bundle.ImportPackageHeader;
import org.eclipse.pde.internal.core.text.bundle.ImportPackageObject;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.PDEUIMessages;
import org.eclipse.pde.internal.ui.util.TextUtil;
import org.osgi.framework.Constants;

import aQute.bnd.osgi.Descriptors.PackageRef;
import aQute.bnd.osgi.Packages;

public class GatherUnusedDependenciesOperation implements IRunnableWithProgress {

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

private final IPluginModelBase fModel;
private ArrayList<Object> fList;
private List<Object> fList;

public GatherUnusedDependenciesOperation(IPluginModelBase model) {
fModel = model;
}

@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {

ImportPackageObject[] packages = null;
Collection<String> exportedPackages = null;
if (ClasspathUtilCore.hasBundleStructure(fModel)) {
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
IManifestHeader header = bundle.getManifestHeader(Constants.IMPORT_PACKAGE);
if (header instanceof ImportPackageHeader) {
packages = ((ImportPackageHeader) header).getPackages();
} else if (header != null && header.getValue() != null) {
header = new ImportPackageHeader(Constants.IMPORT_PACKAGE, header.getValue(), bundle,
TextUtil.getDefaultLineDelimiter());
packages = ((ImportPackageHeader) header).getPackages();
}

header = bundle.getManifestHeader(Constants.EXPORT_PACKAGE);
if (header instanceof ExportPackageHeader) {
exportedPackages = ((ExportPackageHeader) header).getPackageNames();
} else if (header != null && header.getValue() != null) {
header = new ExportPackageHeader(Constants.EXPORT_PACKAGE, header.getValue(), bundle,
TextUtil.getDefaultLineDelimiter());
exportedPackages = ((ExportPackageHeader) header).getPackageNames();
if (!ClasspathUtilCore.hasBundleStructure(fModel)) {
return;
}
Set<String> computedPackages;
try (PdeProjectAnalyzer analyzer = new PdeProjectAnalyzer(fModel.getUnderlyingResource().getProject(), false)) {
analyzer.setImportPackage("*"); //$NON-NLS-1$
analyzer.calcManifest();
Packages imports = analyzer.getImports();
if (imports == null) {
computedPackages = Set.of();
} else {
computedPackages = imports.keySet().stream().map(PackageRef::getFQN).collect(Collectors.toSet());
}
} catch (InterruptedException e) {
throw e;
} catch (Exception e) {
throw new InvocationTargetException(e);
}
ImportPackageObject[] packages = null;
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
IManifestHeader header = bundle.getManifestHeader(Constants.IMPORT_PACKAGE);
if (header instanceof ImportPackageHeader) {
packages = ((ImportPackageHeader) header).getPackages();
} else if (header != null && header.getValue() != null) {
header = new ImportPackageHeader(Constants.IMPORT_PACKAGE, header.getValue(), bundle,
TextUtil.getDefaultLineDelimiter());
packages = ((ImportPackageHeader) header).getPackages();
}
Collection<String> exportedPackages = getExportedPackages(fModel);
IPluginImport[] imports = fModel.getPluginBase().getImports();

int totalWork = imports.length * 3 + (packages != null ? packages.length : 0) + 1;
Expand All @@ -117,58 +120,83 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte
fList = new ArrayList<>();
for (IPluginImport pluginImport : imports) {
if (subMonitor.isCanceled()) {
break;
return;
}
if (isUnused(pluginImport, subMonitor.split(3))) {
if (isUnused(pluginImport, computedPackages, subMonitor.split(3))) {
fList.add(pluginImport);
} else {
usedPlugins.put(pluginImport.getId(), pluginImport);
}
updateMonitor(subMonitor, fList.size());
}

ArrayList<ImportPackageObject> usedPackages = new ArrayList<>();
List<ImportPackageObject> usedPackages = new ArrayList<>();
if (packages != null && !subMonitor.isCanceled()) {
for (ImportPackageObject importPackage : packages) {
if (subMonitor.isCanceled()) {
break;
return;
}
if (isUnused(importPackage, exportedPackages, subMonitor.split(1))) {
if (isUnused(importPackage, exportedPackages, computedPackages, subMonitor.split(1))) {
fList.add(importPackage);
updateMonitor(subMonitor, fList.size());
} else {
usedPackages.add(importPackage);
}
}
}
if (!subMonitor.isCanceled()) {
minimizeDependencies(usedPlugins, usedPackages, subMonitor);
if (subMonitor.isCanceled()) {
return;
}
minimizeDependencies(usedPlugins, usedPackages, subMonitor);
removeBuddies();
removeReexported();
}

private static Collection<String> getExportedPackages(IPluginModelBase model) {
if (model instanceof IBundlePluginModelBase plugin) {
IBundleModel bundleModel = plugin.getBundleModel();
if (bundleModel != null) {
IBundle bundle = bundleModel.getBundle();
IManifestHeader header = bundle.getManifestHeader(Constants.EXPORT_PACKAGE);
if (header instanceof ExportPackageHeader pkg) {
return pkg.getPackageNames();
} else if (header != null && header.getValue() != null) {
ExportPackageHeader pkg = new ExportPackageHeader(Constants.EXPORT_PACKAGE, header.getValue(),
bundle, TextUtil.getDefaultLineDelimiter());
return pkg.getPackageNames();
}
}
}
if (ClasspathUtilCore.hasBundleStructure(fModel)) {
removeBuddies();
removeReexported();
BundleDescription bundleDescription = model.getBundleDescription();
if (bundleDescription != null) {
return Arrays.stream(bundleDescription.getExportPackages()).map(ExportPackageDescription::getName).toList();
}
return List.of();
}

protected void removeBuddies() {
IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle();
IManifestHeader header = bundle.getManifestHeader(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER);
if (header != null) {
String values = header.getValue();
String[] registerBud = values.split("\\s*,\\s*"); //$NON-NLS-1$
List<Object> found = new ArrayList<>();
for (String string : registerBud) {
for (Object obj : fList) {
if (obj instanceof PluginImport) {
String id = ((PluginImport) obj).getId();
if (string.equals(id)) {
found.add(obj);
if (fModel instanceof IBundlePluginModelBase plugin) {
IBundleModel bundleModel = plugin.getBundleModel();
if (bundleModel != null) {
IManifestHeader header = bundleModel.getBundle()
.getManifestHeader(EquinoxModuleDataNamespace.REGISTERED_BUDDY_HEADER);
if (header != null) {
String values = header.getValue();
String[] registerBud = values.split("\\s*,\\s*"); //$NON-NLS-1$
List<Object> found = new ArrayList<>();
for (String string : registerBud) {
for (Object obj : fList) {
if (obj instanceof PluginImport) {
String id = ((PluginImport) obj).getId();
if (string.equals(id)) {
found.add(obj);
}
}
}
}
fList.removeAll(found);
}
}
fList.removeAll(found);
}
}

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

private boolean isUnused(IPluginImport plugin, IProgressMonitor monitor) {
private boolean isUnused(IPluginImport plugin, Set<String> computedPackages, IProgressMonitor monitor) {
IPluginModelBase[] models = PluginJavaSearchUtil.getPluginImports(plugin);
return !provideJavaClasses(models, monitor);
}

private boolean isUnused(ImportPackageObject pkg, Collection<String> exportedPackages, IProgressMonitor monitor) {
if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) {
return false;
}
return !provideJavaClasses(pkg, monitor);
}

private boolean provideJavaClasses(IPluginModelBase[] models, IProgressMonitor monitor) {
try {
IProject project = fModel.getUnderlyingResource().getProject();
if (!project.hasNature(JavaCore.NATURE_ID)) {
return false;
}

IJavaProject jProject = JavaCore.create(project);
IPackageFragment[] packageFragments = PluginJavaSearchUtil.collectPackageFragments(models, jProject, true);
SearchEngine engine = new SearchEngine();
IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject);

SubMonitor subMonitor = SubMonitor.convert(monitor, packageFragments.length * 2);
for (IPackageFragment pkgFragment : packageFragments) {
SubMonitor iterationMonitor = subMonitor.split(2);
if (pkgFragment.hasChildren()) {
Requestor requestor = new Requestor();
SearchPattern pattern = SearchPattern.createPattern(pkgFragment, IJavaSearchConstants.REFERENCES);
if (pattern == null) {
continue;
}
engine.search(pattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope,
requestor, iterationMonitor.split(1));
if (requestor.foundMatches()) {
if (provideJavaClasses(pkgFragment, engine, searchScope,
iterationMonitor.split(1))) {
return true;
}
}
}
}
} catch (CoreException e) {
PDEPlugin.logException(e);
}
return false;
}

private boolean provideJavaClasses(IPackageFragment packageFragment, SearchEngine engine, IJavaSearchScope searchScope, IProgressMonitor monitor) throws JavaModelException, CoreException {
Requestor requestor;
IJavaElement[] children = packageFragment.getChildren();
SubMonitor subMonitor = SubMonitor.convert(monitor, children.length);

for (IJavaElement child : children) {
IType[] types = null;
if (child instanceof ICompilationUnit) {
types = ((ICompilationUnit) child).getAllTypes();
} else if (child instanceof IOrdinaryClassFile) {
types = new IType[] { ((IOrdinaryClassFile) child).getType() };
}
if (types != null) {
SubMonitor iterationMonitor = subMonitor.split(1).setWorkRemaining(types.length);
for (IType type : types) {
requestor = new Requestor();
SearchPattern pattern = SearchPattern.createPattern(type, IJavaSearchConstants.REFERENCES);
if (pattern == null) {
continue;
}
engine.search(pattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope,
requestor, iterationMonitor.split(1));
if (requestor.foundMatches()) {
return true;
}
for (IPluginModelBase model : models) {
Collection<String> exportedPackages = getExportedPackages(model);
for (String exportedPackage : exportedPackages) {
if (computedPackages.contains(exportedPackage)) {
return false;
}
} else {
subMonitor.worked(1);
}
}
return false;
return true;
}

private boolean provideJavaClasses(ImportPackageObject pkg, IProgressMonitor monitor) {
try {
IProject project = fModel.getUnderlyingResource().getProject();

if (!project.hasNature(JavaCore.NATURE_ID)) {
return false;
}

SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
IJavaProject jProject = JavaCore.create(project);
SearchEngine engine = new SearchEngine();
IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject);
Requestor requestor = new Requestor();
String packageName = pkg.getName();

SearchPattern pattern = SearchPattern.createPattern(packageName, IJavaSearchConstants.PACKAGE,
IJavaSearchConstants.REFERENCES, SearchPattern.R_EXACT_MATCH);
if (pattern == null) {
return false;
}
engine.search(
pattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, requestor,
subMonitor.split(1));

if (requestor.foundMatches()) {
return true;
}
} catch (CoreException e) {
PDEPlugin.logException(e);
private boolean isUnused(ImportPackageObject pkg, Collection<String> exportedPackages, Set<String> computedPackages,
IProgressMonitor monitor) {
if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) {
return false;
}
return false;
String name = pkg.getName();
return !computedPackages.contains(name);
}

public ArrayList<Object> getList() {
public List<Object> getList() {
return fList;
}

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

private void minimizeDependencies(HashMap<String, IPluginImport> usedPlugins, ArrayList<ImportPackageObject> usedPackages, IProgressMonitor monitor) {
private void minimizeDependencies(Map<String, IPluginImport> usedPlugins, List<ImportPackageObject> usedPackages,
IProgressMonitor monitor) {
ListIterator<ImportPackageObject> li = usedPackages.listIterator();
while (li.hasNext()) {
ImportPackageObject ipo = li.next();
Expand Down
Loading