blob: 840007bf55b3d2a51141f5af9ae3b68a734886c5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Johannes Ahlers <Johannes.Ahlers@gmx.de> - bug 477677
*******************************************************************************/
package org.eclipse.pde.internal.ui.search.dependencies;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.pde.core.plugin.*;
import org.eclipse.pde.internal.core.ClasspathUtilCore;
import org.eclipse.pde.internal.core.ibundle.*;
import org.eclipse.pde.internal.core.plugin.PluginImport;
import org.eclipse.pde.internal.core.search.PluginJavaSearchUtil;
import org.eclipse.pde.internal.core.text.bundle.*;
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;
public class GatherUnusedDependenciesOperation implements IRunnableWithProgress {
static class Requestor extends SearchRequestor {
boolean fFound = false;
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
fFound = true;
}
public boolean foundMatches() {
return fFound;
}
}
private IPluginModelBase fModel;
private ArrayList<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();
}
}
IPluginImport[] imports = fModel.getPluginBase().getImports();
int totalWork = imports.length * 3 + (packages != null ? packages.length : 0) + 1;
SubMonitor subMonitor = SubMonitor.convert(monitor, totalWork);
HashMap<String, IPluginImport> usedPlugins = new HashMap<>();
fList = new ArrayList<>();
for (IPluginImport pluginImport : imports) {
if (subMonitor.isCanceled())
break;
if (isUnused(pluginImport, subMonitor.split(3))) {
fList.add(pluginImport);
} else
usedPlugins.put(pluginImport.getId(), pluginImport);
updateMonitor(subMonitor, fList.size());
}
ArrayList<ImportPackageObject> usedPackages = new ArrayList<>();
if (packages != null && !subMonitor.isCanceled()) {
for (ImportPackageObject importPackage : packages) {
if (subMonitor.isCanceled())
break;
if (isUnused(importPackage, exportedPackages, subMonitor.split(1))) {
fList.add(importPackage);
updateMonitor(subMonitor, fList.size());
} else
usedPackages.add(importPackage);
}
}
if (!subMonitor.isCanceled()) {
minimizeDependencies(usedPlugins, usedPackages, subMonitor);
}
if (ClasspathUtilCore.hasBundleStructure(fModel)) {
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 (found.size() > 0)
fList.removeAll(found);
}
}
}
private void updateMonitor(IProgressMonitor monitor, int size) {
monitor.setTaskName(PDEUIMessages.UnusedDependencies_analyze + size + " " //$NON-NLS-1$
+ PDEUIMessages.UnusedDependencies_unused + " " //$NON-NLS-1$
+ (size == 1 ? PDEUIMessages.DependencyExtent_singular : PDEUIMessages.DependencyExtent_plural) + " " //$NON-NLS-1$
+ PDEUIMessages.DependencyExtent_found);
}
private boolean isUnused(IPluginImport plugin, 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();
engine.search(SearchPattern.createPattern(pkgFragment, IJavaSearchConstants.REFERENCES),
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();
engine.search(SearchPattern.createPattern(type, IJavaSearchConstants.REFERENCES),
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope,
requestor, iterationMonitor.split(1));
if (requestor.foundMatches()) {
return true;
}
}
} else {
subMonitor.worked(1);
}
}
return false;
}
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();
engine.search(
SearchPattern.createPattern(packageName, IJavaSearchConstants.PACKAGE,
IJavaSearchConstants.REFERENCES, SearchPattern.R_EXACT_MATCH),
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, requestor,
subMonitor.split(1));
if (requestor.foundMatches())
return true;
} catch (CoreException e) {
PDEPlugin.logException(e);
}
return false;
}
public ArrayList<Object> getList() {
return fList;
}
public static void removeDependencies(IPluginModelBase model, Object[] elements) {
ImportPackageHeader pkgHeader = null;
for (Object element : elements) {
if (element instanceof IPluginImport)
try {
model.getPluginBase().remove((IPluginImport) element);
} catch (CoreException e) {
}
else if (element instanceof ImportPackageObject) {
if (pkgHeader == null)
pkgHeader = (ImportPackageHeader) ((ImportPackageObject) element).getHeader();
pkgHeader.removePackage((ImportPackageObject) element);
}
}
}
private void minimizeDependencies(HashMap<String, IPluginImport> usedPlugins, ArrayList<ImportPackageObject> usedPackages, IProgressMonitor monitor) {
ListIterator<ImportPackageObject> li = usedPackages.listIterator();
while (li.hasNext()) {
ImportPackageObject ipo = li.next();
String bundle = ipo.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
if (usedPlugins.containsKey(bundle))
fList.add(ipo);
}
Iterator<String> it = usedPlugins.keySet().iterator();
ArrayDeque<String> plugins = new ArrayDeque<>();
while (it.hasNext())
plugins.push(it.next().toString());
SubMonitor subMonitor = SubMonitor.convert(monitor);
while (!(plugins.isEmpty())) {
String pluginId = plugins.pop();
IPluginModelBase base = PluginRegistry.findModel(pluginId);
if (base == null)
continue;
IPluginImport[] imports = base.getPluginBase().getImports();
SubMonitor iterationMonitor = subMonitor.setWorkRemaining(Math.max(plugins.size() + 1, 5)).split(1)
.setWorkRemaining(imports.length);
for (IPluginImport imp : imports) {
if (imp.isReexported()) {
String reExportedId = imp.getId();
if (reExportedId != null && reExportedId.equals(pluginId)) {
continue;
}
Object pluginImport = usedPlugins.remove(imp.getId());
if (pluginImport != null) {
fList.add(pluginImport);
updateMonitor(iterationMonitor, fList.size());
}
plugins.push(reExportedId);
}
iterationMonitor.worked(1);
}
}
}
}