diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/dependencies/GatherUnusedDependenciesOperation.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/dependencies/GatherUnusedDependenciesOperation.java index 504e52638ef..583e1cd32ce 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/dependencies/GatherUnusedDependenciesOperation.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/dependencies/GatherUnusedDependenciesOperation.java @@ -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 @@ -11,44 +11,39 @@ * Contributors: * IBM Corporation - initial API and implementation * Johannes Ahlers - 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; @@ -56,11 +51,13 @@ 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 { @@ -77,7 +74,7 @@ public boolean foundMatches() { } private final IPluginModelBase fModel; - private ArrayList fList; + private List fList; public GatherUnusedDependenciesOperation(IPluginModelBase model) { fModel = model; @@ -85,29 +82,35 @@ public GatherUnusedDependenciesOperation(IPluginModelBase model) { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { - - ImportPackageObject[] packages = null; - Collection 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 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 exportedPackages = getExportedPackages(fModel); IPluginImport[] imports = fModel.getPluginBase().getImports(); int totalWork = imports.length * 3 + (packages != null ? packages.length : 0) + 1; @@ -117,9 +120,9 @@ 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); @@ -127,13 +130,13 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte updateMonitor(subMonitor, fList.size()); } - ArrayList usedPackages = new ArrayList<>(); + List 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 { @@ -141,34 +144,59 @@ public void run(IProgressMonitor monitor) throws InvocationTargetException, Inte } } } - if (!subMonitor.isCanceled()) { - minimizeDependencies(usedPlugins, usedPackages, subMonitor); + if (subMonitor.isCanceled()) { + return; + } + minimizeDependencies(usedPlugins, usedPackages, subMonitor); + removeBuddies(); + removeReexported(); + } + private static Collection 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 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 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); } } @@ -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 computedPackages, IProgressMonitor monitor) { IPluginModelBase[] models = PluginJavaSearchUtil.getPluginImports(plugin); - return !provideJavaClasses(models, monitor); - } - - private boolean isUnused(ImportPackageObject pkg, Collection 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 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 exportedPackages, Set computedPackages, + IProgressMonitor monitor) { + if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) { + return false; } - return false; + String name = pkg.getName(); + return !computedPackages.contains(name); } - public ArrayList getList() { + public List getList() { return fList; } @@ -330,7 +262,8 @@ public static void removeDependencies(IPluginModelBase model, Object[] elements) } } - private void minimizeDependencies(HashMap usedPlugins, ArrayList usedPackages, IProgressMonitor monitor) { + private void minimizeDependencies(Map usedPlugins, List usedPackages, + IProgressMonitor monitor) { ListIterator li = usedPackages.listIterator(); while (li.hasNext()) { ImportPackageObject ipo = li.next();