Skip to content

Commit 83fbd43

Browse files
committed
Handle class deletion scenario for cleaning up empty exported packages
1 parent 50600c6 commit 83fbd43

6 files changed

Lines changed: 223 additions & 0 deletions

File tree

ui/org.eclipse.pde.ui/plugin.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ rename.type.participant = Manifest Rename Type Participant
158158
rename.package.participant = Manifest Rename Package Participant
159159
move.type.participant = Manifest Move Type Participant
160160
move.package.participant = Manifest Move Package Participant
161+
delete.type.participant = Manifest Delete Type Participant
161162

162163
queryParticipant.name.0 = PDE Java Search Participant
163164
new.profile.name = Target Definition

ui/org.eclipse.pde.ui/plugin.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,22 @@
12251225
</enablement>
12261226
</renameParticipant>
12271227
</extension>
1228+
<extension
1229+
point="org.eclipse.ltk.core.refactoring.deleteParticipants">
1230+
<deleteParticipant
1231+
class="org.eclipse.pde.internal.ui.refactoring.ManifestTypeDeleteParticipant"
1232+
id="org.eclipse.pde.ui.manifestTypeDeleteParticipant"
1233+
name="%delete.type.participant">
1234+
<enablement>
1235+
<with
1236+
variable="element">
1237+
<instanceof
1238+
value="org.eclipse.jdt.core.IType">
1239+
</instanceof>
1240+
</with>
1241+
</enablement>
1242+
</deleteParticipant>
1243+
</extension>
12281244
<extension
12291245
point="org.eclipse.ltk.core.refactoring.moveParticipants">
12301246
<moveParticipant

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2389,6 +2389,8 @@ public class PDEUIMessages extends NLS {
23892389

23902390
public static String ManifestTypeRenameParticipant_composite;
23912391

2392+
public static String ManifestTypeDeleteParticipant_composite;
2393+
23922394
public static String LauncherPage_title;
23932395

23942396
public static String WindowImagesSection_16;

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ MainPreferencePage_test_plugin_pattern_note=This pattern is matched against the
380380
ManifestEditorContributor_externStringsActionName=Externalize Strings...
381381

382382
ManifestTypeRenameParticipant_composite=Rename classes referenced in plug-in manifest files
383+
ManifestTypeDeleteParticipant_composite=Delete classes referenced in plug-in manifest files
383384

384385
ManifestEditor_MatchSection_optional = Optional
385386
ManifestEditor_MatchSection_reexport = Re-export the dependency
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 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+
*******************************************************************************/
14+
package org.eclipse.pde.internal.ui.refactoring;
15+
16+
import java.util.ArrayList;
17+
import java.util.HashMap;
18+
import java.util.HashSet;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Set;
22+
23+
import org.eclipse.core.resources.IFile;
24+
import org.eclipse.core.resources.IFolder;
25+
import org.eclipse.core.resources.IProject;
26+
import org.eclipse.core.resources.IResource;
27+
import org.eclipse.core.runtime.CoreException;
28+
import org.eclipse.core.runtime.IProgressMonitor;
29+
import org.eclipse.jdt.core.IJavaElement;
30+
import org.eclipse.jdt.core.IJavaProject;
31+
import org.eclipse.jdt.core.IPackageFragment;
32+
import org.eclipse.jdt.core.IType;
33+
import org.eclipse.ltk.core.refactoring.Change;
34+
import org.eclipse.ltk.core.refactoring.CompositeChange;
35+
import org.eclipse.pde.internal.core.WorkspaceModelManager;
36+
import org.eclipse.pde.internal.core.project.PDEProject;
37+
import org.eclipse.pde.internal.ui.PDEUIMessages;
38+
39+
public class ManifestTypeDeleteParticipant extends PDEDeleteParticipant {
40+
41+
@Override
42+
protected boolean initialize(Object element) {
43+
if (element instanceof IType type) {
44+
IJavaProject javaProject = (IJavaProject) type.getAncestor(IJavaElement.JAVA_PROJECT);
45+
IProject project = javaProject.getProject();
46+
if (WorkspaceModelManager.isPluginProject(project)) {
47+
fProject = javaProject.getProject();
48+
fElements = new HashMap<>();
49+
fElements.put(element, type.getFullyQualifiedName());
50+
return true;
51+
}
52+
}
53+
return false;
54+
}
55+
56+
@Override
57+
public String getName() {
58+
return PDEUIMessages.ManifestTypeDeleteParticipant_composite;
59+
}
60+
61+
@Override
62+
protected void addChange(CompositeChange result, IProgressMonitor pm) throws CoreException {
63+
IFile file = PDEProject.getManifest(fProject);
64+
if (!file.exists()) {
65+
return;
66+
}
67+
68+
Map<IPackageFragment, List<IType>> deletedByPackage = new HashMap<>();
69+
fElements.forEach((element, fullyQualifiedName) -> {
70+
if (element instanceof IType type) {
71+
IPackageFragment pkg = type.getPackageFragment();
72+
deletedByPackage.computeIfAbsent(pkg, k -> new ArrayList<>()).add(type);
73+
}
74+
});
75+
// Check each package to see if it becomes empty after deletion
76+
for (Map.Entry<IPackageFragment, List<IType>> entry : deletedByPackage.entrySet()) {
77+
IPackageFragment pkg = entry.getKey();
78+
List<IType> deletedTypes = entry.getValue();
79+
80+
try {
81+
if (willPackageBeEmpty(pkg, deletedTypes)) {
82+
Change change = BundleManifestChange.createEmptyPackageChange(file, pkg.getElementName(), pm);
83+
if (change != null) {
84+
result.add(change);
85+
}
86+
}
87+
} catch (CoreException e) {
88+
e.printStackTrace();
89+
}
90+
}
91+
}
92+
93+
/**
94+
* Checks if a package will be empty after deleting the specified types.
95+
* @param pkg the package to check
96+
* @param deletedTypes the types being deleted
97+
* @return true if the package will be empty after deletion
98+
* @throws CoreException if an error occurs accessing package contents
99+
*/
100+
private boolean willPackageBeEmpty(IPackageFragment pkg, List<IType> deletedTypes) throws CoreException {
101+
IJavaElement[] javaChildren = pkg.getChildren();
102+
if (javaChildren.length > deletedTypes.size()) {
103+
return false;
104+
}
105+
// Check for non-Java resources (properties files, XML files, etc.)
106+
Object[] nonJavaResources = pkg.getNonJavaResources();
107+
if (nonJavaResources != null && nonJavaResources.length > 0) {
108+
return false;
109+
}
110+
111+
Set<String> deletedJavaFileNames = new HashSet<>();
112+
for (IType type : deletedTypes) {
113+
// Get the compilation unit (the .java file) containing this type
114+
if (type.getCompilationUnit() != null) {
115+
deletedJavaFileNames.add(type.getCompilationUnit().getElementName());
116+
}
117+
}
118+
// Check the underlying folder for any OTHER files
119+
IResource resource = pkg.getCorrespondingResource();
120+
if (resource instanceof IFolder folder) {
121+
IResource[] members = folder.members();
122+
for (IResource member : members) {
123+
if (member instanceof IFile memberFile) {
124+
String fileName = memberFile.getName();
125+
String extension = memberFile.getFileExtension();
126+
if ("class".equals(extension)) { //$NON-NLS-1$
127+
continue;
128+
}
129+
if ("java".equals(extension) && deletedJavaFileNames.contains(fileName)) { //$NON-NLS-1$
130+
continue;
131+
}
132+
return false;
133+
}
134+
}
135+
}
136+
return true;
137+
}
138+
139+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 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+
*******************************************************************************/
14+
package org.eclipse.pde.internal.ui.refactoring;
15+
16+
import java.util.HashMap;
17+
18+
import org.eclipse.core.resources.IProject;
19+
import org.eclipse.core.runtime.CoreException;
20+
import org.eclipse.core.runtime.IProgressMonitor;
21+
import org.eclipse.core.runtime.OperationCanceledException;
22+
import org.eclipse.jdt.core.IPackageFragment;
23+
import org.eclipse.jdt.core.IType;
24+
import org.eclipse.ltk.core.refactoring.Change;
25+
import org.eclipse.ltk.core.refactoring.CompositeChange;
26+
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
27+
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
28+
import org.eclipse.ltk.core.refactoring.participants.DeleteParticipant;
29+
import org.eclipse.ltk.core.refactoring.participants.ISharableParticipant;
30+
import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
31+
32+
public abstract class PDEDeleteParticipant extends DeleteParticipant implements ISharableParticipant {
33+
34+
protected IProject fProject;
35+
protected HashMap<Object, String> fElements;
36+
37+
@Override
38+
public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
39+
throws OperationCanceledException {
40+
return new RefactoringStatus();
41+
}
42+
43+
@Override
44+
public void addElement(Object element, RefactoringArguments arguments) {
45+
if (element instanceof IType type) {
46+
fElements.put(element, type.getFullyQualifiedName());
47+
} else if (element instanceof IPackageFragment pkg) {
48+
fElements.put(element, pkg.getElementName());
49+
}
50+
}
51+
52+
@Override
53+
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
54+
CompositeChange result = new CompositeChange(getName());
55+
addChange(result, pm);
56+
return (result.getChildren().length == 0) ? null : result;
57+
}
58+
59+
/**
60+
* @throws CoreException may be thrown by overrides
61+
*/
62+
protected abstract void addChange(CompositeChange result, IProgressMonitor pm) throws CoreException;
63+
64+
}

0 commit comments

Comments
 (0)