1 /******************************************************************************* 2 * Copyright (c) 2005, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Johannes Ahlers <Johannes.Ahlers@gmx.de> - bug 477677 14 *******************************************************************************/ 15 package org.eclipse.pde.internal.ui.search.dependencies; 16 17 import java.lang.reflect.InvocationTargetException; 18 import java.util.*; 19 import org.eclipse.core.resources.IProject; 20 import org.eclipse.core.runtime.*; 21 import org.eclipse.jdt.core.*; 22 import org.eclipse.jdt.core.search.*; 23 import org.eclipse.jface.operation.IRunnableWithProgress; 24 import org.eclipse.pde.core.plugin.*; 25 import org.eclipse.pde.internal.core.ClasspathUtilCore; 26 import org.eclipse.pde.internal.core.ibundle.*; 27 import org.eclipse.pde.internal.core.search.PluginJavaSearchUtil; 28 import org.eclipse.pde.internal.core.text.bundle.*; 29 import org.eclipse.pde.internal.ui.PDEPlugin; 30 import org.eclipse.pde.internal.ui.PDEUIMessages; 31 import org.eclipse.pde.internal.ui.util.TextUtil; 32 import org.osgi.framework.Constants; 33 34 public class GatherUnusedDependenciesOperation implements IRunnableWithProgress { 35 36 class Requestor extends SearchRequestor { 37 boolean fFound = false; 38 39 @Override acceptSearchMatch(SearchMatch match)40 public void acceptSearchMatch(SearchMatch match) throws CoreException { 41 fFound = true; 42 } 43 foundMatches()44 public boolean foundMatches() { 45 return fFound; 46 } 47 } 48 49 private IPluginModelBase fModel; 50 private ArrayList<Object> fList; 51 GatherUnusedDependenciesOperation(IPluginModelBase model)52 public GatherUnusedDependenciesOperation(IPluginModelBase model) { 53 fModel = model; 54 } 55 56 @Override run(IProgressMonitor monitor)57 public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { 58 59 ImportPackageObject[] packages = null; 60 Collection<?> exportedPackages = null; 61 if (ClasspathUtilCore.hasBundleStructure(fModel)) { 62 IBundle bundle = ((IBundlePluginModelBase) fModel).getBundleModel().getBundle(); 63 IManifestHeader header = bundle.getManifestHeader(Constants.IMPORT_PACKAGE); 64 if (header instanceof ImportPackageHeader) { 65 packages = ((ImportPackageHeader) header).getPackages(); 66 } else if (header != null && header.getValue() != null) { 67 header = new ImportPackageHeader(Constants.IMPORT_PACKAGE, header.getValue(), bundle, 68 TextUtil.getDefaultLineDelimiter()); 69 packages = ((ImportPackageHeader) header).getPackages(); 70 } 71 72 header = bundle.getManifestHeader(Constants.EXPORT_PACKAGE); 73 if (header instanceof ExportPackageHeader) { 74 exportedPackages = ((ExportPackageHeader) header).getPackageNames(); 75 } else if (header != null && header.getValue() != null) { 76 header = new ExportPackageHeader(Constants.EXPORT_PACKAGE, header.getValue(), bundle, 77 TextUtil.getDefaultLineDelimiter()); 78 exportedPackages = ((ExportPackageHeader) header).getPackageNames(); 79 } 80 } 81 IPluginImport[] imports = fModel.getPluginBase().getImports(); 82 83 int totalWork = imports.length * 3 + (packages != null ? packages.length : 0) + 1; 84 SubMonitor subMonitor = SubMonitor.convert(monitor, totalWork); 85 86 HashMap<String, IPluginImport> usedPlugins = new HashMap<>(); 87 fList = new ArrayList<>(); 88 for (IPluginImport pluginImport : imports) { 89 if (subMonitor.isCanceled()) 90 break; 91 if (isUnused(pluginImport, subMonitor.split(3))) { 92 fList.add(pluginImport); 93 } else 94 usedPlugins.put(pluginImport.getId(), pluginImport); 95 updateMonitor(subMonitor, fList.size()); 96 } 97 98 ArrayList<ImportPackageObject> usedPackages = new ArrayList<>(); 99 if (packages != null && !subMonitor.isCanceled()) { 100 for (ImportPackageObject importPackage : packages) { 101 if (subMonitor.isCanceled()) 102 break; 103 if (isUnused(importPackage, exportedPackages, subMonitor.split(1))) { 104 fList.add(importPackage); 105 updateMonitor(subMonitor, fList.size()); 106 } else 107 usedPackages.add(importPackage); 108 } 109 } 110 if (!subMonitor.isCanceled()) { 111 minimizeDependencies(usedPlugins, usedPackages, subMonitor); 112 } 113 } 114 updateMonitor(IProgressMonitor monitor, int size)115 private void updateMonitor(IProgressMonitor monitor, int size) { 116 monitor.setTaskName(PDEUIMessages.UnusedDependencies_analyze + size + " " //$NON-NLS-1$ 117 + PDEUIMessages.UnusedDependencies_unused + " " //$NON-NLS-1$ 118 + (size == 1 ? PDEUIMessages.DependencyExtent_singular : PDEUIMessages.DependencyExtent_plural) + " " //$NON-NLS-1$ 119 + PDEUIMessages.DependencyExtent_found); 120 } 121 isUnused(IPluginImport plugin, IProgressMonitor monitor)122 private boolean isUnused(IPluginImport plugin, IProgressMonitor monitor) { 123 IPluginModelBase[] models = PluginJavaSearchUtil.getPluginImports(plugin); 124 return !provideJavaClasses(models, monitor); 125 } 126 isUnused(ImportPackageObject pkg, Collection<?> exportedPackages, IProgressMonitor monitor)127 private boolean isUnused(ImportPackageObject pkg, Collection<?> exportedPackages, IProgressMonitor monitor) { 128 if (exportedPackages != null && exportedPackages.contains(pkg.getValue())) { 129 return false; 130 } 131 return !provideJavaClasses(pkg, monitor); 132 } 133 provideJavaClasses(IPluginModelBase[] models, IProgressMonitor monitor)134 private boolean provideJavaClasses(IPluginModelBase[] models, IProgressMonitor monitor) { 135 try { 136 IProject project = fModel.getUnderlyingResource().getProject(); 137 if (!project.hasNature(JavaCore.NATURE_ID)) 138 return false; 139 140 IJavaProject jProject = JavaCore.create(project); 141 IPackageFragment[] packageFragments = PluginJavaSearchUtil.collectPackageFragments(models, jProject, true); 142 SearchEngine engine = new SearchEngine(); 143 IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject); 144 145 SubMonitor subMonitor = SubMonitor.convert(monitor, packageFragments.length * 2); 146 for (IPackageFragment pkgFragment : packageFragments) { 147 SubMonitor iterationMonitor = subMonitor.split(2); 148 if (pkgFragment.hasChildren()) { 149 Requestor requestor = new Requestor(); 150 engine.search(SearchPattern.createPattern(pkgFragment, IJavaSearchConstants.REFERENCES), 151 new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, 152 requestor, iterationMonitor.split(1)); 153 if (requestor.foundMatches()) { 154 if (provideJavaClasses(pkgFragment, engine, searchScope, 155 iterationMonitor.split(1))) { 156 return true; 157 } 158 } 159 } 160 } 161 } catch (CoreException e) { 162 PDEPlugin.logException(e); 163 } 164 return false; 165 } 166 provideJavaClasses(IPackageFragment packageFragment, SearchEngine engine, IJavaSearchScope searchScope, IProgressMonitor monitor)167 private boolean provideJavaClasses(IPackageFragment packageFragment, SearchEngine engine, IJavaSearchScope searchScope, IProgressMonitor monitor) throws JavaModelException, CoreException { 168 Requestor requestor; 169 IJavaElement[] children = packageFragment.getChildren(); 170 SubMonitor subMonitor = SubMonitor.convert(monitor, children.length); 171 172 for (IJavaElement child : children) { 173 IType[] types = null; 174 if (child instanceof ICompilationUnit) { 175 types = ((ICompilationUnit) child).getAllTypes(); 176 } else if (child instanceof IOrdinaryClassFile) { 177 types = new IType[] { ((IOrdinaryClassFile) child).getType() }; 178 } 179 if (types != null) { 180 SubMonitor iterationMonitor = subMonitor.split(1).setWorkRemaining(types.length); 181 for (IType type : types) { 182 requestor = new Requestor(); 183 engine.search(SearchPattern.createPattern(type, IJavaSearchConstants.REFERENCES), 184 new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, 185 requestor, iterationMonitor.split(1)); 186 if (requestor.foundMatches()) { 187 return true; 188 } 189 } 190 } else { 191 subMonitor.worked(1); 192 } 193 } 194 return false; 195 } 196 provideJavaClasses(ImportPackageObject pkg, IProgressMonitor monitor)197 private boolean provideJavaClasses(ImportPackageObject pkg, IProgressMonitor monitor) { 198 try { 199 IProject project = fModel.getUnderlyingResource().getProject(); 200 201 if (!project.hasNature(JavaCore.NATURE_ID)) 202 return false; 203 204 SubMonitor subMonitor = SubMonitor.convert(monitor, 1); 205 IJavaProject jProject = JavaCore.create(project); 206 SearchEngine engine = new SearchEngine(); 207 IJavaSearchScope searchScope = PluginJavaSearchUtil.createSeachScope(jProject); 208 Requestor requestor = new Requestor(); 209 String packageName = pkg.getName(); 210 211 engine.search( 212 SearchPattern.createPattern(packageName, IJavaSearchConstants.PACKAGE, 213 IJavaSearchConstants.REFERENCES, SearchPattern.R_EXACT_MATCH), 214 new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, requestor, 215 subMonitor.split(1)); 216 217 if (requestor.foundMatches()) 218 return true; 219 } catch (CoreException e) { 220 PDEPlugin.logException(e); 221 } 222 return false; 223 } 224 getList()225 public ArrayList<Object> getList() { 226 return fList; 227 } 228 removeDependencies(IPluginModelBase model, Object[] elements)229 public static void removeDependencies(IPluginModelBase model, Object[] elements) { 230 ImportPackageHeader pkgHeader = null; 231 for (Object element : elements) { 232 if (element instanceof IPluginImport) 233 try { 234 model.getPluginBase().remove((IPluginImport) element); 235 } catch (CoreException e) { 236 } 237 else if (element instanceof ImportPackageObject) { 238 if (pkgHeader == null) 239 pkgHeader = (ImportPackageHeader) ((ImportPackageObject) element).getHeader(); 240 pkgHeader.removePackage((ImportPackageObject) element); 241 } 242 } 243 } 244 minimizeDependencies(HashMap<String, IPluginImport> usedPlugins, ArrayList<ImportPackageObject> usedPackages, IProgressMonitor monitor)245 private void minimizeDependencies(HashMap<String, IPluginImport> usedPlugins, ArrayList<ImportPackageObject> usedPackages, IProgressMonitor monitor) { 246 ListIterator<ImportPackageObject> li = usedPackages.listIterator(); 247 while (li.hasNext()) { 248 ImportPackageObject ipo = li.next(); 249 String bundle = ipo.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE); 250 if (usedPlugins.containsKey(bundle)) 251 fList.add(ipo); 252 } 253 254 Iterator<String> it = usedPlugins.keySet().iterator(); 255 ArrayDeque<String> plugins = new ArrayDeque<>(); 256 while (it.hasNext()) 257 plugins.push(it.next().toString()); 258 SubMonitor subMonitor = SubMonitor.convert(monitor); 259 while (!(plugins.isEmpty())) { 260 String pluginId = plugins.pop(); 261 IPluginModelBase base = PluginRegistry.findModel(pluginId); 262 if (base == null) 263 continue; 264 IPluginImport[] imports = base.getPluginBase().getImports(); 265 SubMonitor iterationMonitor = subMonitor.setWorkRemaining(Math.max(plugins.size() + 1, 5)).split(1) 266 .setWorkRemaining(imports.length); 267 for (IPluginImport imp : imports) { 268 if (imp.isReexported()) { 269 String reExportedId = imp.getId(); 270 if (reExportedId != null && reExportedId.equals(pluginId)) { 271 continue; 272 } 273 Object pluginImport = usedPlugins.remove(imp.getId()); 274 if (pluginImport != null) { 275 fList.add(pluginImport); 276 updateMonitor(iterationMonitor, fList.size()); 277 } 278 plugins.push(reExportedId); 279 } 280 iterationMonitor.worked(1); 281 } 282 } 283 } 284 } 285