XMLCleanupHandler.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* 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:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.internal.ui.handlers;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import org.sandbox.jdt.internal.corext.fix.helper.XMLCleanupService;
/**
* Command handler for XML cleanup that works independently of JDT.
* This allows XML cleanup to be triggered directly on PDE files or projects.
*/
public class XMLCleanupHandler extends AbstractHandler {
private static final String PLUGIN_ID = "sandbox_xml_cleanup";
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
ISelection selection = HandlerUtil.getCurrentSelection(event);
if (!(selection instanceof IStructuredSelection)) {
return null;
}
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
List<IResource> resources = new ArrayList<>();
// Collect all resources from selection
for (Object element : structuredSelection.toList()) {
IResource resource = getResource(element);
if (resource != null) {
resources.add(resource);
}
}
if (resources.isEmpty()) {
MessageDialog.openInformation(
HandlerUtil.getActiveShell(event),
"XML Cleanup",
"No valid files or projects selected.");
return null;
}
// Run cleanup in a background job
Job job = new Job("XML Cleanup") {
@Override
protected IStatus run(IProgressMonitor monitor) {
return performCleanup(resources, monitor);
}
};
job.setUser(true);
job.schedule();
return null;
}
/**
* Get IResource from a selection element.
*/
private IResource getResource(Object element) {
if (element instanceof IResource) {
return (IResource) element;
}
if (element instanceof IAdaptable) {
return ((IAdaptable) element).getAdapter(IResource.class);
}
return null;
}
/**
* Create and configure the XML cleanup service.
*/
private XMLCleanupService createXMLCleanupService() {
XMLCleanupService service = new XMLCleanupService();
// TODO: Add preference support for indent option
service.setEnableIndent(false);
return service;
}
/**
* Perform the actual cleanup on selected resources.
*/
private IStatus performCleanup(List<IResource> resources, IProgressMonitor monitor) {
XMLCleanupService service = createXMLCleanupService();
int totalFilesProcessed = 0;
int totalProjects = 0;
int totalFiles = 0;
// Count total work
for (IResource resource : resources) {
if (resource instanceof IProject) {
totalProjects++;
} else if (resource instanceof IFile) {
totalFiles++;
}
}
// Estimate work units (projects are more expensive)
int totalWork = totalProjects * 50 + totalFiles;
monitor.beginTask("Cleaning up PDE XML files", totalWork);
try {
for (IResource resource : resources) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (resource instanceof IProject) {
IProject project = (IProject) resource;
monitor.subTask("Processing project: " + project.getName());
int filesChanged = service.processProject(project, monitor);
totalFilesProcessed += filesChanged;
monitor.worked(50);
} else if (resource instanceof IFile) {
IFile file = (IFile) resource;
monitor.subTask("Processing file: " + file.getName());
try {
boolean changed = service.processFile(file, monitor);
if (changed) {
totalFilesProcessed++;
}
} catch (CoreException e) {
return new Status(IStatus.ERROR, PLUGIN_ID,
"Error processing file: " + file.getName(), e);
}
monitor.worked(1);
}
}
String message = String.format("XML Cleanup completed. %d file(s) processed and updated.",
totalFilesProcessed);
return new Status(IStatus.OK, PLUGIN_ID, message);
} catch (Exception e) {
return new Status(IStatus.ERROR, PLUGIN_ID,
"Error during XML cleanup", e);
} finally {
monitor.done();
}
}
}