diff --git a/ui/org.eclipse.pde.ui/plugin.properties b/ui/org.eclipse.pde.ui/plugin.properties
index 66cb7c89f7..7c58d3d329 100644
--- a/ui/org.eclipse.pde.ui/plugin.properties
+++ b/ui/org.eclipse.pde.ui/plugin.properties
@@ -158,6 +158,8 @@ rename.type.participant = Manifest Rename Type Participant
rename.package.participant = Manifest Rename Package Participant
move.type.participant = Manifest Move Type Participant
move.package.participant = Manifest Move Package Participant
+delete.type.participant = Manifest Delete Type Participant
+delete.package.participant = Manifest Delete Package Participant
queryParticipant.name.0 = PDE Java Search Participant
new.profile.name = Target Definition
diff --git a/ui/org.eclipse.pde.ui/plugin.xml b/ui/org.eclipse.pde.ui/plugin.xml
index 4a34c0b6ac..10d9d1a201 100644
--- a/ui/org.eclipse.pde.ui/plugin.xml
+++ b/ui/org.eclipse.pde.ui/plugin.xml
@@ -1225,6 +1225,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
packages,
+ IProgressMonitor monitor) throws CoreException {
+ List packageNames = packages.stream().map(IPackageFragment::getElementName).toList();
+ if (packageNames.isEmpty()) {
+ return null;
+ }
try {
Bundle bundle = getBundle(file, monitor);
if (bundle == null) {
@@ -82,8 +88,8 @@ public static Change createEmptyPackageChange(IFile file, String packageName, IP
bundle.getModel().addModelChangedListener(listener);
BasePackageHeader header = (BasePackageHeader) bundle.getManifestHeader(Constants.EXPORT_PACKAGE);
- if (header != null && header.hasPackage(packageName)) {
- removePackage(header, packageName);
+ if (header != null) {
+ packageNames.stream().filter(header::hasPackage).forEach(name -> removePackage(header, name));
}
TextEdit[] operations = listener.getTextOperations();
if (operations.length > 0) {
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestPackageDeleteParticipant.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestPackageDeleteParticipant.java
new file mode 100644
index 0000000000..9d4e1aa349
--- /dev/null
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestPackageDeleteParticipant.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2026 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
+ *******************************************************************************/
+package org.eclipse.pde.internal.ui.refactoring;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
+import org.eclipse.pde.internal.core.WorkspaceModelManager;
+import org.eclipse.pde.internal.core.project.PDEProject;
+import org.eclipse.pde.internal.ui.PDEPlugin;
+import org.eclipse.pde.internal.ui.PDEUIMessages;
+
+/**
+ * Handles package deletion in PDE plugin projects. Updates the bundle's
+ * MANIFEST.MF after deleting packages.
+ */
+public class ManifestPackageDeleteParticipant extends PDEDeleteParticipant {
+
+ private IProject fProject;
+ private Set fPackages = new HashSet<>();
+
+ @Override
+ protected boolean initialize(Object element) {
+ IPackageFragment fragment = (IPackageFragment) element;
+ try {
+ if (!fragment.containsJavaResources()) {
+ return false;
+ }
+ } catch (JavaModelException e) {
+ PDEPlugin.logException(e);
+ }
+ fProject = fragment.getJavaProject().getProject();
+ if (WorkspaceModelManager.isPluginProject(fProject)) {
+ fPackages.add(fragment);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void addElement(Object element, RefactoringArguments arguments) {
+ fPackages.add((IPackageFragment) element);
+ }
+
+ @Override
+ public String getName() {
+ return PDEUIMessages.ManifestPackageDeleteParticipant_packageDelete;
+ }
+
+ @Override
+ protected void addChanges(CompositeChange result, IProgressMonitor pm) throws CoreException {
+ IFile file = PDEProject.getManifest(fProject);
+ if (file.exists()) {
+ Change change = BundleManifestChange.createDeletePackagesChange(file, fPackages, pm);
+ if (change != null) {
+ result.add(change);
+ }
+ }
+ }
+
+}
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeDeleteParticipant.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeDeleteParticipant.java
new file mode 100644
index 0000000000..ada50e5754
--- /dev/null
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeDeleteParticipant.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2026 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
+ *******************************************************************************/
+package org.eclipse.pde.internal.ui.refactoring;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
+import org.eclipse.pde.internal.core.WorkspaceModelManager;
+import org.eclipse.pde.internal.core.project.PDEProject;
+import org.eclipse.pde.internal.ui.PDEUIMessages;
+
+/**
+ * Handles class deletion in PDE plugin projects. Updates the bundle's MANIFEST.MF
+ * when a package becomes empty after deleting types.
+ */
+public class ManifestTypeDeleteParticipant extends PDEDeleteParticipant {
+
+ private IProject fProject;
+ private Set fTypes = new LinkedHashSet<>();
+
+ @Override
+ protected boolean initialize(Object element) {
+ IType type = (IType) element;
+ IJavaProject javaProject = type.getJavaProject();
+ if (javaProject != null) {
+ fProject = javaProject.getProject();
+ if (WorkspaceModelManager.isPluginProject(fProject)) {
+ fTypes.add(type);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void addElement(Object element, RefactoringArguments arguments) {
+ IType type = (IType) element;
+ fTypes.add(type);
+ }
+
+ @Override
+ public String getName() {
+ return PDEUIMessages.ManifestTypeDeleteParticipant_composite;
+ }
+
+ @Override
+ protected void addChanges(CompositeChange result, IProgressMonitor pm) throws CoreException {
+ IFile file = PDEProject.getManifest(fProject);
+ if (!file.exists()) {
+ return;
+ }
+ // Group deleted types by package and collect their compilation units
+ Map> deletedCUsByPackage = new HashMap<>();
+ for (IType type : fTypes) {
+ IPackageFragment pkg = type.getPackageFragment();
+ ICompilationUnit cu = type.getCompilationUnit();
+ if (cu != null) {
+ deletedCUsByPackage.computeIfAbsent(pkg, k -> new HashSet<>()).add(cu);
+ }
+ }
+ // Check each package to see if it becomes empty after deletion
+ List emptiedPackages = deletedCUsByPackage.entrySet().stream().filter(e -> {
+ IPackageFragment pkg = e.getKey();
+ Set deletedCUs = e.getValue();
+ try {
+ return willPackageBeEmpty(pkg, deletedCUs);
+ } catch (CoreException ex) {
+ return false;
+ }
+ }).map(Map.Entry::getKey).toList();
+
+ Change change = BundleManifestChange.createDeletePackagesChange(file, emptiedPackages, pm);
+ if (change != null) {
+ result.add(change);
+ }
+
+ }
+
+ /**
+ * Checks if a package will be empty after deleting the specified compilation units.
+ *
+ * @param pkg the package to check
+ * @param deletedCUs the compilation units being deleted
+ * @return true if the package will be empty after deletion
+ * @throws CoreException if an error occurs accessing package contents
+ */
+ private boolean willPackageBeEmpty(IPackageFragment pkg, Set deletedCUs) throws CoreException {
+ // Check for non-Java resources (properties files, XML files, etc.)
+ Object[] nonJavaResources = pkg.getNonJavaResources();
+ if (nonJavaResources != null && nonJavaResources.length > 0) {
+ return false;
+ }
+ // Check if any compilation unit in the package is NOT being deleted
+ ICompilationUnit[] compilationUnits = pkg.getCompilationUnits();
+ for (ICompilationUnit cu : compilationUnits) {
+ if (!deletedCUs.contains(cu)) {
+ // This compilation unit is not being deleted, so package is not
+ // empty
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeMoveParticipant.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeMoveParticipant.java
index f32dba6bc3..3fcaa40577 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeMoveParticipant.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/ManifestTypeMoveParticipant.java
@@ -115,7 +115,7 @@ protected void addChange(CompositeChange result, IProgressMonitor pm) throws Cor
int totalFiles = children.length;
int movingCount = movingTypes.size();
if (totalFiles == movingCount) {
- Change change = BundleManifestChange.createEmptyPackageChange(file, pkg.getElementName(), pm);
+ Change change = BundleManifestChange.createDeletePackagesChange(file, List.of(pkg), pm);
if (change != null) {
result.add(change);
}
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/PDEDeleteParticipant.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/PDEDeleteParticipant.java
new file mode 100644
index 0000000000..6606be753a
--- /dev/null
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/refactoring/PDEDeleteParticipant.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2026 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
+ *******************************************************************************/
+package org.eclipse.pde.internal.ui.refactoring;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.DeleteParticipant;
+import org.eclipse.ltk.core.refactoring.participants.ISharableParticipant;
+
+public abstract class PDEDeleteParticipant extends DeleteParticipant implements ISharableParticipant {
+
+ @Override
+ public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
+ throws OperationCanceledException {
+ return new RefactoringStatus();
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+ CompositeChange result = new CompositeChange(getName());
+ addChanges(result, pm);
+ return result.getChildren().length == 0 ? null : result;
+ }
+
+ protected abstract void addChanges(CompositeChange result, IProgressMonitor pm) throws CoreException;
+
+}