1 /******************************************************************************* 2 * Copyright (c) 2005, 2018 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 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 487988 14 * Martin Karpisek <martin.karpisek@gmail.com> - Bug 351356 15 *******************************************************************************/ 16 package org.eclipse.pde.internal.ui.editor.plugin; 17 18 import java.util.*; 19 import org.eclipse.core.resources.IProject; 20 import org.eclipse.core.resources.IResource; 21 import org.eclipse.core.runtime.*; 22 import org.eclipse.jdt.core.*; 23 import org.eclipse.jdt.ui.ISharedImages; 24 import org.eclipse.jdt.ui.JavaUI; 25 import org.eclipse.jdt.ui.actions.FindReferencesInWorkingSetAction; 26 import org.eclipse.jdt.ui.actions.ShowInPackageViewAction; 27 import org.eclipse.jface.action.*; 28 import org.eclipse.jface.viewers.*; 29 import org.eclipse.jface.window.Window; 30 import org.eclipse.osgi.service.resolver.*; 31 import org.eclipse.pde.core.*; 32 import org.eclipse.pde.core.plugin.*; 33 import org.eclipse.pde.core.target.NameVersionDescriptor; 34 import org.eclipse.pde.internal.core.*; 35 import org.eclipse.pde.internal.core.bundle.BundlePluginBase; 36 import org.eclipse.pde.internal.core.ibundle.*; 37 import org.eclipse.pde.internal.core.project.PDEProject; 38 import org.eclipse.pde.internal.core.text.bundle.*; 39 import org.eclipse.pde.internal.core.util.PDEJavaHelper; 40 import org.eclipse.pde.internal.ui.*; 41 import org.eclipse.pde.internal.ui.editor.*; 42 import org.eclipse.pde.internal.ui.editor.context.InputContextManager; 43 import org.eclipse.pde.internal.ui.parts.ConditionalListSelectionDialog; 44 import org.eclipse.pde.internal.ui.parts.TablePart; 45 import org.eclipse.pde.internal.ui.search.dependencies.UnusedDependenciesAction; 46 import org.eclipse.pde.internal.ui.util.SWTUtil; 47 import org.eclipse.pde.internal.ui.util.TextUtil; 48 import org.eclipse.search.ui.NewSearchUI; 49 import org.eclipse.swt.SWT; 50 import org.eclipse.swt.custom.BusyIndicator; 51 import org.eclipse.swt.graphics.Image; 52 import org.eclipse.swt.layout.GridData; 53 import org.eclipse.swt.widgets.*; 54 import org.eclipse.ui.*; 55 import org.eclipse.ui.actions.ActionFactory; 56 import org.eclipse.ui.forms.widgets.FormToolkit; 57 import org.eclipse.ui.forms.widgets.Section; 58 import org.eclipse.ui.progress.UIJob; 59 import org.osgi.framework.Constants; 60 import org.osgi.framework.Version; 61 62 public class ImportPackageSection extends TableSection { 63 64 private static final int ADD_INDEX = 0; 65 private static final int REMOVE_INDEX = 1; 66 private static final int PROPERTIES_INDEX = 2; 67 68 private ImportPackageHeader fHeader; 69 70 class ImportItemWrapper { 71 Object fUnderlying; 72 ImportItemWrapper(Object underlying)73 public ImportItemWrapper(Object underlying) { 74 fUnderlying = underlying; 75 } 76 77 @Override toString()78 public String toString() { 79 return getName(); 80 } 81 82 @Override equals(Object obj)83 public boolean equals(Object obj) { 84 if (obj instanceof ImportItemWrapper) { 85 ImportItemWrapper item = (ImportItemWrapper) obj; 86 return getName().equals(item.getName()); 87 } 88 return false; 89 } 90 91 @Override hashCode()92 public int hashCode() { 93 return getName().hashCode(); 94 } 95 getName()96 public String getName() { 97 if (fUnderlying instanceof ExportPackageDescription) 98 return ((ExportPackageDescription) fUnderlying).getName(); 99 if (fUnderlying instanceof IPackageFragment) 100 return ((IPackageFragment) fUnderlying).getElementName(); 101 if (fUnderlying instanceof ExportPackageObject) 102 return ((ExportPackageObject) fUnderlying).getName(); 103 return null; 104 } 105 getVersion()106 public Version getVersion() { 107 if (fUnderlying instanceof ExportPackageDescription) 108 return ((ExportPackageDescription) fUnderlying).getVersion(); 109 if (fUnderlying instanceof ExportPackageObject) { 110 String version = ((ExportPackageObject) fUnderlying).getVersion(); 111 if (version != null) 112 return new Version(version); 113 } 114 return null; 115 } 116 hasVersion()117 boolean hasVersion() { 118 return hasEPD() && ((ExportPackageDescription) fUnderlying).getVersion() != null; 119 } 120 hasEPD()121 boolean hasEPD() { 122 return fUnderlying instanceof ExportPackageDescription; 123 } 124 } 125 126 class ImportPackageContentProvider implements IStructuredContentProvider { 127 @Override getElements(Object parent)128 public Object[] getElements(Object parent) { 129 if (fHeader == null) { 130 Bundle bundle = (Bundle) getBundle(); 131 fHeader = (ImportPackageHeader) bundle.getManifestHeader(Constants.IMPORT_PACKAGE); 132 } 133 return fHeader == null ? new Object[0] : fHeader.getPackages(); 134 } 135 } 136 137 class ImportPackageDialogLabelProvider extends LabelProvider { 138 @Override getImage(Object element)139 public Image getImage(Object element) { 140 return JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_PACKAGE); 141 } 142 143 @Override getText(Object element)144 public String getText(Object element) { 145 StringBuilder buffer = new StringBuilder(); 146 ImportItemWrapper p = (ImportItemWrapper) element; 147 buffer.append(p.getName()); 148 Version version = p.getVersion(); 149 if (version != null && !Version.emptyVersion.equals(version)) { 150 // Bug 183417 - Bidi3.3: Elements' labels in the extensions page in the fragment manifest characters order is incorrect 151 // add RTL zero length character just before the ( and the LTR character just after to ensure: 152 // 1. The leading parenthesis takes proper orientation when running in bidi configuration 153 // 2. The bundle's version is always displayed as LTR. Otherwise if qualifier contains an alpha, 154 // it would be displayed incorrectly when running RTL. 155 buffer.append(' '); 156 buffer.append(PDELabelProvider.formatVersion(version.toString())); 157 } 158 return buffer.toString(); 159 } 160 } 161 162 private TableViewer fPackageViewer; 163 164 private Action fAddAction; 165 private Action fGoToAction; 166 private Action fRemoveAction; 167 private Action fPropertiesAction; 168 ImportPackageSection(PDEFormPage page, Composite parent)169 public ImportPackageSection(PDEFormPage page, Composite parent) { 170 super(page, parent, Section.DESCRIPTION, new String[] {PDEUIMessages.ImportPackageSection_add, PDEUIMessages.ImportPackageSection_remove, PDEUIMessages.ImportPackageSection_properties}); 171 } 172 isFragment()173 private boolean isFragment() { 174 IPluginModelBase model = (IPluginModelBase) getPage().getPDEEditor().getAggregateModel(); 175 return model != null && model.isFragmentModel(); 176 } 177 178 @Override createClient(Section section, FormToolkit toolkit)179 protected void createClient(Section section, FormToolkit toolkit) { 180 section.setText(PDEUIMessages.ImportPackageSection_required); 181 if (isFragment()) 182 section.setDescription(PDEUIMessages.ImportPackageSection_descFragment); 183 else 184 section.setDescription(PDEUIMessages.ImportPackageSection_desc); 185 186 Composite container = createClientContainer(section, 2, toolkit); 187 createViewerPartControl(container, SWT.MULTI, 2, toolkit); 188 TablePart tablePart = getTablePart(); 189 fPackageViewer = tablePart.getTableViewer(); 190 fPackageViewer.setContentProvider(new ImportPackageContentProvider()); 191 fPackageViewer.setLabelProvider(PDEPlugin.getDefault().getLabelProvider()); 192 fPackageViewer.setComparator(new ViewerComparator() { 193 @Override 194 public int compare(Viewer viewer, Object e1, Object e2) { 195 String s1 = e1.toString(); 196 String s2 = e2.toString(); 197 if (s1.contains(" ")) //$NON-NLS-1$ 198 s1 = s1.substring(0, s1.indexOf(' ')); 199 if (s2.contains(" ")) //$NON-NLS-1$ 200 s2 = s2.substring(0, s2.indexOf(' ')); 201 return super.compare(viewer, s1, s2); 202 } 203 }); 204 toolkit.paintBordersFor(container); 205 section.setClient(container); 206 section.setLayout(FormLayoutFactory.createClearGridLayout(false, 1)); 207 section.setLayoutData(new GridData(GridData.FILL_BOTH)); 208 makeActions(); 209 210 IBundleModel model = getBundleModel(); 211 fPackageViewer.setInput(model); 212 model.addModelChangedListener(this); 213 updateButtons(); 214 } 215 216 @Override doGlobalAction(String actionId)217 public boolean doGlobalAction(String actionId) { 218 219 if (!isEditable()) { 220 return false; 221 } 222 223 if (actionId.equals(ActionFactory.DELETE.getId())) { 224 handleRemove(); 225 return true; 226 } 227 if (actionId.equals(ActionFactory.CUT.getId())) { 228 // delete here and let the editor transfer 229 // the selection to the clipboard 230 handleRemove(); 231 return false; 232 } 233 if (actionId.equals(ActionFactory.PASTE.getId())) { 234 doPaste(); 235 return true; 236 } 237 return super.doGlobalAction(actionId); 238 } 239 240 @Override canPaste(Object targetObject, Object[] sourceObjects)241 protected boolean canPaste(Object targetObject, Object[] sourceObjects) { 242 // Only non-duplicate import packages can be pasted 243 for (Object sourceObject : sourceObjects) { 244 // Only import package objects are allowed 245 if ((sourceObject instanceof ImportPackageObject) == false) { 246 return false; 247 } 248 // Note: Should check if the package fragment represented by the 249 // import package object exists 250 // (like in org.eclipse.pde.internal.ui.editor.plugin.ImportPackageSection.setElements(ConditionalListSelectionDialog)) 251 // However, the operation is too performance intensive as it 252 // requires searching all workspace and target plug-in 253 254 // If the import package header is not defined, no import packages 255 // have been defined yet 256 if (fHeader == null) { 257 continue; 258 } 259 // Only import package objects that have not already been 260 // specified are allowed (no duplicates) 261 ImportPackageObject importPackageObject = (ImportPackageObject) sourceObject; 262 if (fHeader.hasPackage(importPackageObject.getName())) { 263 return false; 264 } 265 } 266 return true; 267 } 268 269 @Override dispose()270 public void dispose() { 271 IBundleModel model = getBundleModel(); 272 if (model != null) 273 model.removeModelChangedListener(this); 274 super.dispose(); 275 } 276 277 @Override doPaste(Object targetObject, Object[] sourceObjects)278 protected void doPaste(Object targetObject, Object[] sourceObjects) { 279 // Get the model 280 IBundleModel model = getBundleModel(); 281 // Ensure the model is defined 282 if (model == null) { 283 return; 284 } 285 // Get the bundle 286 IBundle bundle = model.getBundle(); 287 // Paste all source objects 288 for (Object sourceObject : sourceObjects) { 289 if (sourceObject instanceof ImportPackageObject) { 290 ImportPackageObject importPackageObject = (ImportPackageObject) sourceObject; 291 // Import package object 292 // Adjust all the source object transient field values to 293 // acceptable values 294 importPackageObject.reconnect(model, fHeader, getVersionAttribute()); 295 // Add the object to the header 296 if (fHeader == null) { 297 // Import package header not defined yet 298 // Define one 299 // Value will get inserted into a new import package object 300 // created by a factory 301 // Value needs to be empty string so no import package 302 // object is created as the initial value 303 bundle.setHeader(getImportedPackageHeader(), ""); //$NON-NLS-1$ 304 } 305 // Add the import package to the header 306 fHeader.addPackage(importPackageObject); 307 } 308 } 309 } 310 getImportedPackageHeader()311 private String getImportedPackageHeader() { 312 return Constants.IMPORT_PACKAGE; 313 } 314 315 @Override selectionChanged(IStructuredSelection sel)316 protected void selectionChanged(IStructuredSelection sel) { 317 getPage().getPDEEditor().setSelection(sel); 318 updateButtons(); 319 } 320 updateButtons()321 private void updateButtons() { 322 Object[] selected = fPackageViewer.getStructuredSelection().toArray(); 323 int size = selected.length; 324 TablePart tablePart = getTablePart(); 325 tablePart.setButtonEnabled(ADD_INDEX, isEditable()); 326 tablePart.setButtonEnabled(REMOVE_INDEX, isEditable() && size > 0); 327 tablePart.setButtonEnabled(PROPERTIES_INDEX, shouldEnableProperties(selected)); 328 } 329 330 @Override handleDoubleClick(IStructuredSelection selection)331 protected void handleDoubleClick(IStructuredSelection selection) { 332 handleGoToPackage(selection); 333 } 334 335 @Override buttonSelected(int index)336 protected void buttonSelected(int index) { 337 switch (index) { 338 case ADD_INDEX : 339 handleAdd(); 340 break; 341 case REMOVE_INDEX : 342 handleRemove(); 343 break; 344 case PROPERTIES_INDEX : 345 handleOpenProperties(); 346 } 347 } 348 getPackageFragment(ISelection sel)349 private IPackageFragment getPackageFragment(ISelection sel) { 350 if (sel instanceof IStructuredSelection) { 351 IStructuredSelection selection = (IStructuredSelection) sel; 352 if (selection.size() != 1) 353 return null; 354 355 IBaseModel model = getPage().getModel(); 356 if (!(model instanceof IPluginModelBase)) 357 return null; 358 359 return PDEJavaHelper.getPackageFragment(((PackageObject) selection.getFirstElement()).getName(), ((IPluginModelBase) model).getPluginBase().getId(), getPage().getPDEEditor().getCommonProject()); 360 } 361 return null; 362 } 363 handleGoToPackage(ISelection selection)364 private void handleGoToPackage(ISelection selection) { 365 IPackageFragment frag = getPackageFragment(selection); 366 if (frag != null) 367 try { 368 IViewPart part = PDEPlugin.getActivePage().showView(JavaUI.ID_PACKAGES); 369 ShowInPackageViewAction action = new ShowInPackageViewAction(part.getSite()); 370 action.run(frag); 371 } catch (PartInitException e) { 372 } 373 } 374 handleOpenProperties()375 private void handleOpenProperties() { 376 Object[] selected = fPackageViewer.getStructuredSelection().toArray(); 377 ImportPackageObject first = (ImportPackageObject) selected[0]; 378 DependencyPropertiesDialog dialog = new DependencyPropertiesDialog(isEditable(), first); 379 dialog.create(); 380 PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IHelpContextIds.IMPORTED_PACKAGE_PROPERTIES); 381 SWTUtil.setDialogSize(dialog, 400, -1); 382 if (selected.length == 1) 383 dialog.setTitle(((ImportPackageObject) selected[0]).getName()); 384 else 385 dialog.setTitle(PDEUIMessages.ExportPackageSection_props); 386 if (dialog.open() == Window.OK && isEditable()) { 387 String newVersion = dialog.getVersion(); 388 boolean newOptional = dialog.isOptional(); 389 for (Object selectedObject : selected) { 390 ImportPackageObject object = (ImportPackageObject) selectedObject; 391 if (!newVersion.equals(object.getVersion())) 392 object.setVersion(newVersion); 393 if (!newOptional == object.isOptional()) 394 object.setOptional(newOptional); 395 } 396 } 397 } 398 handleRemove()399 private void handleRemove() { 400 Object[] removed = fPackageViewer.getStructuredSelection().toArray(); 401 for (Object removedObject : removed) { 402 fHeader.removePackage((PackageObject) removedObject); 403 } 404 } 405 handleAdd()406 private void handleAdd() { 407 final ConditionalListSelectionDialog dialog = new ConditionalListSelectionDialog(PDEPlugin.getActiveWorkbenchShell(), new ImportPackageDialogLabelProvider(), PDEUIMessages.ImportPackageSection_dialogButtonLabel); 408 Runnable runnable = () -> { 409 setElements(dialog); 410 dialog.setMultipleSelection(true); 411 dialog.setMessage(PDEUIMessages.ImportPackageSection_exported); 412 dialog.setTitle(PDEUIMessages.ImportPackageSection_selection); 413 dialog.create(); 414 PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IHelpContextIds.IMPORT_PACKAGES); 415 SWTUtil.setDialogSize(dialog, 400, 500); 416 }; 417 418 BusyIndicator.showWhile(Display.getCurrent(), runnable); 419 if (dialog.open() == Window.OK) { 420 Object[] selected = dialog.getResult(); 421 if (fHeader != null) { 422 Set<String> names = new HashSet<>(); // set of String names, do not allow the same package to be added twice 423 for (int i = 0; i < selected.length; i++) { 424 ImportPackageObject impObject = null; 425 if (selected[i] instanceof ImportItemWrapper) 426 selected[i] = ((ImportItemWrapper) selected[i]).fUnderlying; 427 428 if (selected[i] instanceof ExportPackageDescription) 429 impObject = new ImportPackageObject(fHeader, (ExportPackageDescription) selected[i], getVersionAttribute()); 430 else if (selected[i] instanceof IPackageFragment) { 431 // non exported package 432 IPackageFragment fragment = ((IPackageFragment) selected[i]); 433 impObject = new ImportPackageObject(fHeader, fragment.getElementName(), null, getVersionAttribute()); 434 } else if (selected[i] instanceof ExportPackageObject) { 435 ExportPackageObject epo = (ExportPackageObject) selected[i]; 436 impObject = new ImportPackageObject(fHeader, epo.getName(), epo.getVersion(), getVersionAttribute()); 437 } 438 if (impObject != null && names.add(impObject.getName())) 439 fHeader.addPackage(impObject); 440 } 441 } else { 442 getBundle().setHeader(Constants.IMPORT_PACKAGE, getValue(selected)); 443 } 444 } 445 } 446 getValue(Object[] objects)447 private String getValue(Object[] objects) { 448 StringBuilder buffer = new StringBuilder(); 449 for (int i = 0; i < objects.length; i++) { 450 if (!(objects[i] instanceof ImportItemWrapper)) 451 continue; 452 Version version = ((ImportItemWrapper) objects[i]).getVersion(); 453 if (buffer.length() > 0) 454 buffer.append("," + getLineDelimiter() + " "); //$NON-NLS-1$ //$NON-NLS-2$ 455 buffer.append(((ImportItemWrapper) objects[i]).getName()); 456 if (version != null && !version.equals(Version.emptyVersion)) { 457 buffer.append(";"); //$NON-NLS-1$ 458 buffer.append(getVersionAttribute()); 459 buffer.append("=\""); //$NON-NLS-1$ 460 buffer.append(version.toString()); 461 buffer.append("\""); //$NON-NLS-1$ 462 } 463 } 464 return buffer.toString(); 465 } 466 setElements(ConditionalListSelectionDialog dialog)467 private void setElements(ConditionalListSelectionDialog dialog) { 468 Set<String> forbidden = getForbiddenIds(); 469 boolean allowJava = "true".equals(getBundle().getHeader(ICoreConstants.ECLIPSE_JREBUNDLE)); //$NON-NLS-1$ 470 471 ArrayList<ImportItemWrapper> elements = new ArrayList<>(); 472 ArrayList<ImportItemWrapper> conditional = new ArrayList<>(); 473 IPluginModelBase[] models = PluginRegistry.getActiveModels(); 474 Set<NameVersionDescriptor> nameVersions = new HashSet<>(); // Set of NameVersionDescriptors, used to remove duplicate entries 475 476 for (IPluginModelBase pluginModel : models) { 477 BundleDescription desc = pluginModel.getBundleDescription(); 478 479 // If the current model is a fragment, it can export packages only if its parent has hasExtensibleAPI set 480 if (isFragmentThatCannotExportPackages(pluginModel)) 481 continue; 482 483 String id = desc == null ? null : desc.getSymbolicName(); 484 if (id == null || forbidden.contains(id)) 485 continue; 486 487 ExportPackageDescription[] exported = desc.getExportPackages(); 488 for (ExportPackageDescription exportedPackage : exported) { 489 String name = exportedPackage.getName(); 490 NameVersionDescriptor nameVersion = new NameVersionDescriptor(exportedPackage.getName(), exportedPackage.getVersion().toString(), NameVersionDescriptor.TYPE_PACKAGE); 491 if (("java".equals(name) || name.startsWith("java.")) && !allowJava) //$NON-NLS-1$ //$NON-NLS-2$ 492 continue; 493 if (nameVersions.add(nameVersion) && (fHeader == null || !fHeader.hasPackage(name))) 494 elements.add(new ImportItemWrapper(exportedPackage)); 495 } 496 IPluginModelBase model = (IPluginModelBase) getPage().getPDEEditor().getAggregateModel(); 497 if (model instanceof IBundlePluginModelBase) { 498 IBundleModel bmodel = ((IBundlePluginModelBase) model).getBundleModel(); 499 if (bmodel != null) { 500 ExportPackageHeader header = (ExportPackageHeader) bmodel.getBundle().getManifestHeader(Constants.EXPORT_PACKAGE); 501 if (header != null) { 502 ExportPackageObject[] pkgs = header.getPackages(); 503 for (ExportPackageObject pkg : pkgs) { 504 String name = pkg.getName(); 505 String version = pkg.getVersion(); 506 NameVersionDescriptor nameVersion = new NameVersionDescriptor(name, version, NameVersionDescriptor.TYPE_PACKAGE); 507 if (nameVersions.add(nameVersion) && (fHeader == null || !fHeader.hasPackage(name))) 508 elements.add(new ImportItemWrapper(pkg)); 509 } 510 } 511 } 512 513 } 514 } 515 for (IPluginModelBase model : models) { 516 try { 517 // add un-exported packages in workspace non-binary plug-ins 518 IResource resource = model.getUnderlyingResource(); 519 IProject project = resource != null ? resource.getProject() : null; 520 if (project == null || !project.hasNature(JavaCore.NATURE_ID) || WorkspaceModelManager.isBinaryProject(project) || !PDEProject.getManifest(project).exists()) 521 continue; 522 523 // If the current model is a fragment, it can export packages only if its parent has hasExtensibleAPI set 524 if (isFragmentThatCannotExportPackages(model)) 525 continue; 526 527 IJavaProject jp = JavaCore.create(project); 528 IPackageFragmentRoot[] roots = jp.getPackageFragmentRoots(); 529 for (int j = 0; j < roots.length; j++) { 530 if (roots[j].getKind() == IPackageFragmentRoot.K_SOURCE || (roots[j].getKind() == IPackageFragmentRoot.K_BINARY && !roots[j].isExternal())) { 531 IJavaElement[] children = roots[j].getChildren(); 532 for (IJavaElement child : children) { 533 IPackageFragment f = (IPackageFragment) child; 534 String name = f.getElementName(); 535 NameVersionDescriptor nameVersion = new NameVersionDescriptor(name, null, NameVersionDescriptor.TYPE_PACKAGE); 536 if (name.equals("")) //$NON-NLS-1$ 537 name = "."; //$NON-NLS-1$ 538 if (nameVersions.add(nameVersion) && (f.hasChildren() || f.getNonJavaResources().length > 0)) 539 conditional.add(new ImportItemWrapper(f)); 540 } 541 } 542 } 543 } catch (CoreException e) { 544 } 545 } 546 dialog.setElements(elements.toArray()); 547 dialog.setConditionalElements(conditional.toArray()); 548 } 549 550 /** 551 * Returns whether the provided plug-in model is a fragment that cannot export 552 * its packages to other bundles (<code>hasExtensibleAPI</code> is not set). Will 553 * return false if the model does not represent a fragment. 554 * 555 * @param fragment the model to test 556 * @return <code>true</code> if the model is a fragment that cannot export packages 557 */ isFragmentThatCannotExportPackages(IPluginModelBase fragment)558 private boolean isFragmentThatCannotExportPackages(IPluginModelBase fragment) { 559 if (!fragment.isFragmentModel()) { 560 // Not a fragment 561 return false; 562 } 563 BundleDescription bundleDescription = fragment.getBundleDescription(); 564 if (bundleDescription == null) { 565 // Classic plugin, do not change the behavior 566 return false; 567 } 568 HostSpecification hostSpec = bundleDescription.getHost(); 569 if (hostSpec == null) { 570 // Not a fragment 571 return false; 572 } 573 BundleDescription[] hosts = hostSpec.getHosts(); 574 // At least one of fragment hosts has to have extensible API 575 for (BundleDescription host : hosts) { 576 if (ClasspathUtilCore.hasExtensibleAPI(PluginRegistry.findModel(host))) 577 return false; 578 } 579 // Fragment that cannot export 580 return true; 581 } 582 583 @Override modelChanged(final IModelChangedEvent event)584 public void modelChanged(final IModelChangedEvent event) { 585 if (event.getChangeType() == IModelChangedEvent.WORLD_CHANGED) { 586 fHeader = null; 587 markStale(); 588 return; 589 } 590 591 // Model change may have come from a non UI thread such as the auto add dependencies operation. See bug 333533 592 UIJob job = new UIJob("Update package imports") { //$NON-NLS-1$ 593 @Override 594 public IStatus runInUIThread(IProgressMonitor monitor) { 595 if (Constants.IMPORT_PACKAGE.equals(event.getChangedProperty())) { 596 refresh(); 597 // Bug 171896 598 // Since the model sends a CHANGE event instead of 599 // an INSERT event on the very first addition to the empty table 600 // Selection should fire here to take this first insertion into account 601 Object lastElement = fPackageViewer.getElementAt(fPackageViewer.getTable().getItemCount() - 1); 602 if (lastElement != null) { 603 fPackageViewer.setSelection(new StructuredSelection(lastElement)); 604 } 605 return Status.OK_STATUS; 606 } 607 608 Object[] objects = event.getChangedObjects(); 609 for (Object changedObject : objects) { 610 if (changedObject instanceof ImportPackageObject) { 611 ImportPackageObject object = (ImportPackageObject) changedObject; 612 switch (event.getChangeType()) { 613 case IModelChangedEvent.INSERT : 614 fPackageViewer.remove(object); // If another thread has modified the header, avoid creating a duplicate 615 fPackageViewer.add(object); 616 fPackageViewer.setSelection(new StructuredSelection(object)); 617 fPackageViewer.getTable().setFocus(); 618 break; 619 case IModelChangedEvent.REMOVE : 620 Table table = fPackageViewer.getTable(); 621 int index = table.getSelectionIndex(); 622 fPackageViewer.remove(object); 623 table.setSelection(index < table.getItemCount() ? index : table.getItemCount() - 1); 624 updateButtons(); 625 break; 626 default : 627 fPackageViewer.refresh(object); 628 } 629 } 630 } 631 return Status.OK_STATUS; 632 } 633 }; 634 job.setSystem(true); 635 job.schedule(); 636 } 637 638 @Override refresh()639 public void refresh() { 640 fPackageViewer.refresh(); 641 super.refresh(); 642 } 643 makeActions()644 private void makeActions() { 645 fAddAction = new Action(PDEUIMessages.RequiresSection_add) { 646 @Override 647 public void run() { 648 handleAdd(); 649 } 650 }; 651 fAddAction.setEnabled(isEditable()); 652 fGoToAction = new Action(PDEUIMessages.ImportPackageSection_goToPackage) { 653 @Override 654 public void run() { 655 handleGoToPackage(fPackageViewer.getStructuredSelection()); 656 } 657 }; 658 fRemoveAction = new Action(PDEUIMessages.RequiresSection_delete) { 659 @Override 660 public void run() { 661 handleRemove(); 662 } 663 }; 664 fRemoveAction.setEnabled(isEditable()); 665 666 fPropertiesAction = new Action(PDEUIMessages.ImportPackageSection_propertyAction) { 667 @Override 668 public void run() { 669 handleOpenProperties(); 670 } 671 }; 672 } 673 674 @Override fillContextMenu(IMenuManager manager)675 protected void fillContextMenu(IMenuManager manager) { 676 final IStructuredSelection selection = fPackageViewer.getStructuredSelection(); 677 manager.add(fAddAction); 678 boolean singleSelection = selection.size() == 1; 679 if (singleSelection) 680 manager.add(fGoToAction); 681 manager.add(new Separator()); 682 if (!selection.isEmpty()) 683 manager.add(fRemoveAction); 684 getPage().getPDEEditor().getContributor().contextMenuAboutToShow(manager); 685 686 if (((IModel) getPage().getModel()).getUnderlyingResource() != null) { 687 manager.add(new Separator()); 688 if (singleSelection) { 689 manager.add(new Action(PDEUIMessages.DependencyExtentSearchResultPage_referencesInPlugin) { 690 @Override 691 public void run() { 692 doReferenceSearch(selection); 693 } 694 }); 695 } 696 manager.add(new UnusedDependenciesAction((IPluginModelBase) getPage().getModel(), false)); 697 } 698 699 if (shouldEnableProperties(fPackageViewer.getStructuredSelection().toArray())) { 700 manager.add(new Separator()); 701 manager.add(fPropertiesAction); 702 } 703 } 704 doReferenceSearch(final ISelection sel)705 private void doReferenceSearch(final ISelection sel) { 706 IPackageFragmentRoot[] roots = null; 707 try { 708 roots = getSourceRoots(); 709 } catch (JavaModelException e) { 710 } 711 final IPackageFragment fragment = getPackageFragment(sel); 712 if (fragment != null && roots != null) { 713 IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager(); 714 IWorkingSet set = manager.createWorkingSet("temp", roots); //$NON-NLS-1$ 715 new FindReferencesInWorkingSetAction(getPage().getEditorSite(), new IWorkingSet[] {set}).run(fragment); 716 manager.removeWorkingSet(set); 717 } else if (sel instanceof IStructuredSelection) { 718 IStructuredSelection selection = (IStructuredSelection) sel; 719 PackageObject importObject = (PackageObject) selection.getFirstElement(); 720 NewSearchUI.runQueryInBackground(new BlankQuery(importObject)); 721 } 722 } 723 getSourceRoots()724 private IPackageFragmentRoot[] getSourceRoots() throws JavaModelException { 725 ArrayList<IPackageFragmentRoot> result = new ArrayList<>(); 726 IProject project = getPage().getPDEEditor().getCommonProject(); 727 // would normally return array of size 0, but by returning null can optimize the search to run faster. 728 if (project == null) { 729 return null; 730 } 731 IPackageFragmentRoot[] roots = JavaCore.create(project).getPackageFragmentRoots(); 732 for (int i = 0; i < roots.length; i++) { 733 if (roots[i].getKind() == IPackageFragmentRoot.K_SOURCE || (roots[i].isArchive() && !roots[i].isExternal())) 734 result.add(roots[i]); 735 } 736 return result.toArray(new IPackageFragmentRoot[result.size()]); 737 } 738 getBundleContext()739 private BundleInputContext getBundleContext() { 740 InputContextManager manager = getPage().getPDEEditor().getContextManager(); 741 return (BundleInputContext) manager.findContext(BundleInputContext.CONTEXT_ID); 742 } 743 getBundleModel()744 private IBundleModel getBundleModel() { 745 BundleInputContext context = getBundleContext(); 746 return (context != null) ? (IBundleModel) context.getModel() : null; 747 748 } 749 getLineDelimiter()750 private String getLineDelimiter() { 751 BundleInputContext inputContext = getBundleContext(); 752 if (inputContext != null) { 753 return inputContext.getLineDelimiter(); 754 } 755 return TextUtil.getDefaultLineDelimiter(); 756 } 757 getBundle()758 private IBundle getBundle() { 759 IBundleModel model = getBundleModel(); 760 return (model != null) ? model.getBundle() : null; 761 } 762 getVersionAttribute()763 private String getVersionAttribute() { 764 return getVersionAttribute(getBundle()); 765 } 766 getVersionAttribute(IBundle bundle)767 private String getVersionAttribute(IBundle bundle) { 768 int manifestVersion = BundlePluginBase.getBundleManifestVersion(bundle); 769 return (manifestVersion < 2) ? ICoreConstants.PACKAGE_SPECIFICATION_VERSION : Constants.VERSION_ATTRIBUTE; 770 } 771 getForbiddenIds()772 private Set<String> getForbiddenIds() { 773 HashSet<String> set = new HashSet<>(); 774 IPluginModelBase model = (IPluginModelBase) getPage().getPDEEditor().getAggregateModel(); 775 if (model == null) { 776 return set; 777 } 778 String id = model.getPluginBase().getId(); 779 if (id != null) 780 set.add(id); 781 IPluginImport[] imports = model.getPluginBase().getImports(); 782 State state = TargetPlatformHelper.getState(); 783 for (IPluginImport pluginImport : imports) { 784 addDependency(state, pluginImport.getId(), set); 785 } 786 return set; 787 } 788 addDependency(State state, String bundleID, Set<String> set)789 private void addDependency(State state, String bundleID, Set<String> set) { 790 if (bundleID == null || !set.add(bundleID)) 791 return; 792 793 BundleDescription desc = state.getBundle(bundleID, null); 794 if (desc == null) 795 return; 796 797 BundleDescription[] fragments = desc.getFragments(); 798 for (BundleDescription fragment : fragments) { 799 addDependency(state, fragment.getSymbolicName(), set); 800 } 801 802 BundleSpecification[] specs = desc.getRequiredBundles(); 803 for (BundleSpecification spec : specs) { 804 if (spec.isResolved() && spec.isExported()) { 805 addDependency(state, spec.getName(), set); 806 } 807 } 808 } 809 810 @Override createCount()811 protected boolean createCount() { 812 return true; 813 } 814 shouldEnableProperties(Object[] selected)815 private boolean shouldEnableProperties(Object[] selected) { 816 if (selected.length == 0) 817 return false; 818 if (selected.length == 1) 819 return true; 820 821 String version = ((ImportPackageObject) selected[0]).getVersion(); 822 boolean optional = ((ImportPackageObject) selected[0]).isOptional(); 823 for (int i = 1; i < selected.length; i++) { 824 ImportPackageObject object = (ImportPackageObject) selected[i]; 825 if (version == null) { 826 if (object.getVersion() != null || !(optional == object.isOptional())) { 827 return false; 828 } 829 } else if (!version.equals(object.getVersion()) || !(optional == object.isOptional())) { 830 return false; 831 } 832 } 833 return true; 834 } 835 836 } 837