2

I'm creating an Eclipse batch application that applies source cleanups (equivalent of right-click menu Source -> Clean Up) from a profile file. It's an XML file listing the rules and whether they're activated or not. It can be exported from Window -> Preferences and then Java -> Code Style -> Clean Up.

It seems to give good results but one of the cleanups, Organize Imports, fails with an exception.

It can be turned on and off in this line of the profile file :

<setting id="cleanup.organize_imports" value="false"/> <!-- fails if set to true -->

The project tree is:

C:\USERS\xxxxx\GIT\REFACTORING-CLI
│   .classpath
│   .gitignore
│   .project
│   build.properties
│   LICENSE
│   plugin.xml
│   pom.xml
│
├───META-INF
│       MANIFEST.MF
│
└───src
    └───main
        └───java
            └───io
                └───github
                    └───xxxxxxxxx
                        └───refactoring
                            └───cli
                                    Activator.java
                                    CleanupRunner.java
                                    HeadlessCleanupApp.java

Here's the root pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
           http://maven.apache.org/POM/4.0.0
           https://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>io.github.xxxxxxxxx</groupId>
  <artifactId>io.github.xxxxxxxxx.refactoring.cli</artifactId>
  <version>1.0.0</version>
  <packaging>eclipse-plugin</packaging>
  <properties>
    <tycho.version>5.0.0</tycho.version>
    <eclipse.release.repo>https://download.eclipse.org/releases/2025-09</eclipse.release.repo>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <repositories>
    <repository>
      <id>eclipse-release</id>
      <url>${eclipse.release.repo}</url>
      <layout>p2</layout>
    </repository>
  </repositories>

  <build>
    <extensions>
      <extension>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>tycho-maven-plugin</artifactId>
        <version>${tycho.version}</version>
      </extension>
    </extensions>

    <plugins>
      <plugin>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>tycho-p2-director-plugin</artifactId>
        <version>${tycho.version}</version>
      </plugin>
    </plugins>
  </build>

</project>

Here's the plugin.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
  <extension id="app" point="org.eclipse.core.runtime.applications">
    <application>
      <run class="io.github.xxxxxxxxx.refactoring.cli.HeadlessCleanupApp"/>
    </application>
  </extension>
</plugin>

Here's the build.properties file:`

bin.includes = META-INF/,\
               .,\
               plugin.xml
source.. = src/
output.. = bin/

Here's the META-INF/MANIFEST.MF file:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Refactoring CLI
Bundle-SymbolicName: io.github.xxxxxxxxx.refactoring.cli;singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: io.github.xxxxxxxxx.refactoring.cli.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-21
Automatic-Module-Name: io.github.xxxxxxxxx.refactoring.cli
Bundle-ActivationPolicy: lazy
Require-Bundle: 
 org.eclipse.core.runtime,
 org.eclipse.core.resources,
 org.eclipse.jdt.core,
 org.eclipse.jdt.ui,
 org.eclipse.jdt.core.manipulation,
 org.eclipse.ltk.core.refactoring,
 org.eclipse.text,
 org.eclipse.jface,
 org.eclipse.equinox.app

Here's the entry point of the application HeadlessCleanupApp :

package io.github.xxxxxxxxx.refactoring.cli;

import org.eclipse.equinox.app.*;
import java.nio.file.*;
import java.util.*;

public class HeadlessCleanupApp implements IApplication {

    @Override
    public Object start(IApplicationContext context) throws Exception {
        String[] args = (String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS);

        if (args == null || args.length == 0) {
            System.err.println("Missing arguments. Usage:");
            System.err.println("  --source <level> --profile <file> <projectRoot> [--classpath <entries>]");
            return Integer.valueOf(1);
        }

        String sourceLevel = null;
        String profilePath = null;
        String projectRootPath = null;
        List<String> extraClasspath = new ArrayList<>();

        int i = 0;
        while (i < args.length) {
            String arg = args[i];
            if ("--source".equals(arg) && i + 1 < args.length) {
                sourceLevel = args[++i];
            } else if ("--profile".equals(arg) && i + 1 < args.length) {
                profilePath = args[++i];
            } else if ("--classpath".equals(arg) && i + 1 < args.length) {
                String[] cpEntries = args[++i].split(System.getProperty("path.separator"));
                for (String entry : cpEntries) {
                    extraClasspath.add(entry);
                }
            } else {
                projectRootPath = arg;
            }
            i++;
        }

        if (sourceLevel == null || profilePath == null || projectRootPath == null) {
            System.err.println("Missing required parameters.");
            return Integer.valueOf(1);
        }

        Path projectRoot = Paths.get(projectRootPath);
        Path profileFile = Paths.get(profilePath);

        CleanupRunner runner =
                new CleanupRunner(projectRoot, profileFile, sourceLevel, extraClasspath);

        runner.run();

        return Integer.valueOf(0);
    }

    @Override
    public void stop() {
        // no-op
    }
}

The class applying the cleanups is CleanupRunner as follows :

package io.github.xxxxxxxxx.refactoring.cli;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

import javax.xml.parsers.*;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.internal.corext.fix.*;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions;
import org.eclipse.jdt.ui.cleanup.ICleanUp;
import org.eclipse.ltk.core.refactoring.*;
import org.eclipse.text.edits.TextEdit;
import org.w3c.dom.*;

public class CleanupRunner {

    private final Path projectRoot;
    private final Path profileFile;
    private final String sourceLevel;
    private final List<String> extraClasspath;

    public CleanupRunner(Path projectRoot, Path profileFile, String sourceLevel, List<String> extraClasspath) {
        this.projectRoot = projectRoot;
        this.profileFile = profileFile;
        this.sourceLevel = sourceLevel;
        this.extraClasspath = extraClasspath;
    }

    public List<Path> run() throws Exception {

        LoggingMonitor monitor = new LoggingMonitor();

        System.out.println("=== Starting cleanup ===");
        System.out.println("Project root: " + projectRoot);
        System.out.println("Profile file: " + profileFile);
        System.out.println("Source level: " + sourceLevel);

        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IWorkspaceRoot wsRoot = workspace.getRoot();

        System.out.println("Creating temporary workspace project...");
        IProject project = wsRoot.getProject("refactoring-cli-project");
        if (!project.exists()) {
            project.create(monitor);
        }
        project.open(monitor);

        addJavaNature(project);
        System.out.println("Java nature enabled.");

        System.out.println("Detecting source folders...");
        Set<Path> sourceFolders = detectSourceFolders(projectRoot);
        System.out.println("Detected " + sourceFolders.size() + " source folders.");

        System.out.println("Linking source folders...");
        Map<IPath, IFolder> linkedFolders = linkSourceFolders(project, sourceFolders);

        project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
        System.out.println("Workspace refreshed.");

        IJavaProject javaProject = JavaCore.create(project);

        setEncoding(project);
        System.out.println("Encoding set to UTF-8.");

        System.out.println("Configuring classpath...");
        configureClasspath(javaProject, linkedFolders);

        System.out.println("Configuring compiler options...");
        configureCompilerOptions(javaProject);

        System.out.println("Collecting compilation units...");
        List<ICompilationUnit> units = collectCompilationUnits(javaProject);
        System.out.println("Found " + units.size() + " compilation units.");

        if (units.isEmpty()) {
            System.out.println("Nothing to clean.");
            return new ArrayList<>();
        }

        System.out.println("Loading cleanup settings...");
        Map<String, String> cleanupSettings = loadCleanupSettingsFromProfile(profileFile);

        System.out.println("Loading available cleanups...");
        CleanUpRegistry registry = JavaPlugin.getDefault().getCleanUpRegistry();
        ICleanUp[] cleanUps = registry.createCleanUps(null);
        System.out.println("Loaded " + cleanUps.length + " cleanup modules.");

        MapCleanUpOptions options = new MapCleanUpOptions(cleanupSettings);

        for (ICleanUp cleanUp : cleanUps) {
            cleanUp.setOptions(options);
        }

        System.out.println("Preparing refactoring...");
        CleanUpRefactoring refactoring = new CleanUpRefactoring();

        for (ICompilationUnit unit : units) {
            refactoring.addCompilationUnit(unit);
        }

        for (ICleanUp cleanUp : cleanUps) {
            refactoring.addCleanUp(cleanUp);
        }

        System.out.println("Checking initial conditions...");
        RefactoringStatus initStatus = refactoring.checkInitialConditions(monitor);
        System.out.println("Initial condition status: " + initStatus);

        if (initStatus.hasFatalError()) {
            System.err.println("Fatal error during initial conditions.");
            return new ArrayList<>();
        }

        System.out.println("Checking final conditions...");
        RefactoringStatus finalStatus = refactoring.checkFinalConditions(monitor);
        System.out.println("Final condition status: " + finalStatus);

        if (finalStatus.hasFatalError()) {
            System.err.println("Fatal error during final conditions.");
            return new ArrayList<>();
        }

        System.out.println("Creating change...");
        Change change = refactoring.createChange(monitor);
        if (change == null) {
            System.out.println("No changes generated.");
            return new ArrayList<>();
        }

        System.out.println("Collecting changed files...");
        List<Path> changed = collectChangedFiles(change);
        System.out.println("Pending changes: " + changed.size());

        for (Path p : changed) {
            System.out.println("Will modify: " + p);
        }

        System.out.println("Initializing change...");
        change.initializeValidationData(monitor);

        System.out.println("Validating change...");
        RefactoringStatus status = change.isValid(monitor);
        System.out.println("Validation result: " + status);

        if (status.hasFatalError()) {
            System.err.println("Change validation failed.");
            return changed;
        }

        System.out.println("Applying change...");
        change.perform(monitor);

        System.out.println("Saving workspace...");
        ResourcesPlugin.getWorkspace().save(true, monitor);

        System.out.println("=== Cleanup complete ===");
        System.out.println("Modified " + changed.size() + " files.");

        return changed;
    }

    private void addJavaNature(IProject project) throws CoreException {
        IProjectDescription desc = project.getDescription();
        String[] natures = desc.getNatureIds();
        boolean hasJavaNature = false;
        for (String n : natures) {
            if (JavaCore.NATURE_ID.equals(n)) {
                hasJavaNature = true;
                break;
            }
        }
        if (!hasJavaNature) {
            String[] newNatures = new String[natures.length + 1];
            System.arraycopy(natures, 0, newNatures, 0, natures.length);
            newNatures[natures.length] = JavaCore.NATURE_ID;
            desc.setNatureIds(newNatures);
            project.setDescription(desc, null);
        }
    }

    private Set<Path> detectSourceFolders(Path root) throws IOException {
        Set<Path> sourceFolders = new LinkedHashSet<>();

        Files.walkFileTree(root, new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (!file.toString().endsWith(".java")) {
                    return FileVisitResult.CONTINUE;
                }

                Path parent = file.getParent();
                if (parent == null) {
                    return FileVisitResult.CONTINUE;
                }

                for (Path src : sourceFolders) {
                    if (parent.startsWith(src)) {
                        return FileVisitResult.CONTINUE;
                    }
                }

                String packageName = readPackageName(file);
                Path sourceFolder = inferSourceFolder(file, packageName);

                if (sourceFolder != null && Files.isDirectory(sourceFolder)) {
                    sourceFolders.add(sourceFolder.normalize());
                }

                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {
                throw new UncheckedIOException(exc);
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
                if (exc != null) {
                    throw new UncheckedIOException(exc);
                }
                return FileVisitResult.CONTINUE;
            }
        });

        return sourceFolders;
    }

    private String readPackageName(Path file) throws IOException {
        char[] source = Files.readString(file, StandardCharsets.UTF_8).toCharArray();
        ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
        parser.setSource(source);
        parser.setKind(ASTParser.K_COMPILATION_UNIT);

        CompilationUnit unit = (CompilationUnit) parser.createAST(null);

        if (unit.getPackage() != null) {
            return unit.getPackage().getName().getFullyQualifiedName();
        }

        return "";
    }

    private Path inferSourceFolder(Path file, String packageName) {
        Path parent = file.getParent();
        if (parent == null) {
            return null;
        }

        if (packageName == null || packageName.isEmpty()) {
            return parent;
        }

        String[] segments = packageName.split("\\.");
        Path pkgPath = parent;

        for (int i = segments.length - 1; i >= 0; i--) {
            if (pkgPath == null || pkgPath.getFileName() == null || !pkgPath.getFileName().toString().equals(segments[i])) {
                break;
            }
            pkgPath = pkgPath.getParent();
        }

        return pkgPath;
    }

    private Map<IPath, IFolder> linkSourceFolders(IProject project, Set<Path> folders) throws CoreException {
        LinkedHashMap<IPath, IFolder> links = new LinkedHashMap<>();
        int index = 0;
        for (Path folder : folders) {
            String linkName = "src_" + index;
            index = index + 1;
            IFolder linked = project.getFolder(linkName);
            if (!linked.exists()) {
                linked.createLink(new org.eclipse.core.runtime.Path(folder.toString()), IResource.REPLACE, null);
            }
            links.put(new org.eclipse.core.runtime.Path(folder.toString()), linked);
        }
        return links;
    }

    private void configureClasspath(IJavaProject javaProject, Map<IPath, IFolder> linkedFolders) throws CoreException {
        List<IClasspathEntry> entries = new ArrayList<>();

        for (IFolder folder : linkedFolders.values()) {
            entries.add(JavaCore.newSourceEntry(folder.getFullPath()));
        }

        entries.add(JavaCore.newContainerEntry(
                new org.eclipse.core.runtime.Path("org.eclipse.jdt.launching.JRE_CONTAINER")));

        for (String cp : extraClasspath) {
            org.eclipse.core.runtime.Path path = new org.eclipse.core.runtime.Path(cp);
            entries.add(JavaCore.newLibraryEntry(path, null, null));
        }

        javaProject.setRawClasspath(entries.toArray(new IClasspathEntry[0]), null);
    }

    private void configureCompilerOptions(IJavaProject javaProject) {
        Map<String, String> options = javaProject.getOptions(false);
        options.put(JavaCore.COMPILER_SOURCE, sourceLevel);
        options.put(JavaCore.COMPILER_COMPLIANCE, sourceLevel);
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, sourceLevel);
        javaProject.setOptions(options);
    }

    private void setEncoding(IProject project) throws CoreException {
        project.setDefaultCharset(StandardCharsets.UTF_8.name(), null);
    }

    private List<ICompilationUnit> collectCompilationUnits(IJavaProject javaProject) throws CoreException {
        List<ICompilationUnit> result = new ArrayList<>();
        IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
        for (IPackageFragmentRoot root : roots) {
            if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
                IJavaElement[] children = root.getChildren();
                for (IJavaElement el : children) {
                    if (el instanceof IPackageFragment pkg) {
                        ICompilationUnit[] units = pkg.getCompilationUnits();
                        Collections.addAll(result, units);
                    }
                }
            }
        }
        return result;
    }

    private Map<String, String> loadCleanupSettingsFromProfile(Path profile) throws IOException {
        Map<String, String> settings = new LinkedHashMap<>();

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(false);
            factory.setIgnoringComments(true);
            factory.setIgnoringElementContentWhitespace(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            try (InputStream in = Files.newInputStream(profile)) {
                Document document = builder.parse(in);

                NodeList nodes = document.getElementsByTagName("setting");
                for (int i = 0; i < nodes.getLength(); i++) {
                    Node node = nodes.item(i);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (org.w3c.dom.Element) node;

                        String id = element.getAttribute("id");
                        String value = element.getAttribute("value");

                        if (id != null && !id.isEmpty()) {
                            settings.put(id, value);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new IOException("Failed to parse cleanup profile XML: " + profile, e);
        }

        return settings;
    }


    private List<Path> collectChangedFiles(Change change) throws CoreException {
        List<Path> result = new ArrayList<>();
        if (change instanceof CompositeChange composite) {
            Change[] children = composite.getChildren();
            for (Change child : children) {
                result.addAll(collectChangedFiles(child));
            }
        } else if (change instanceof TextFileChange tfc) {
            TextEdit edit = tfc.getEdit();
            if (edit != null && edit.hasChildren()) {
                IFile file = tfc.getFile();
                result.add(Paths.get(file.getLocation().toOSString()));
            }
        }
        return result;
    }


    private static class LoggingMonitor extends ProgressMonitorWrapper {

        public LoggingMonitor() {
            super(new NullProgressMonitor());
        }

        @Override
        public void beginTask(String name, int totalWork) {
            System.out.println("BEGIN: " + name + " (" + totalWork + ")");
        }

        @Override
        public void worked(int work) {
            System.out.println("WORK: " + work);
        }

        @Override
        public void done() {
            System.out.println("DONE");
        }
    }

}

There's a minimal Activator class :

package io.github.xxxxxxxxx.refactoring.cli;

import org.eclipse.core.runtime.Plugin;

public class Activator extends Plugin {
}

In Eclipse RCP, in the MANIFEST GUI, I choose "Launch an Eclipse application in Debug mode".

Launch an Eclipse application in Debug mode

Alternatively, after building with mvn package, and dropping the jar into dropins folder of Eclipse, I launch :

eclipsec -nosplash -clean -data C:\Users\xxxxx\.refactoring-workspace -application io.github.xxxxxxxxx.refactoring.cli.app --source 21 --profile C:\Users\xxxxx\Downloads\source_cleanup_profile.xml C:\Users\xxxxx\git\codebase_to_cleanup

Here's the log from C:\Users\xxxxx\.refactoring-workspace\.metadata\.log :

!SESSION 2025-11-23 11:01:57.007 -----------------------------------------------
eclipse.buildId=4.37.0.20250905-1455
java.version=21.0.9
java.vendor=Eclipse Adoptium
BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=fr_FR

!ENTRY org.eclipse.osgi 4 0 2025-11-23 11:01:57.264
!MESSAGE The -clean (osgi.clean) option was not successful. Unable to clean the storage area: C:\Users\xxxxx\Downloads\eclipse-rcp-2025-09-R-win32-x86_64\eclipse\configuration\org.eclipse.osgi

!ENTRY ch.qos.logback.classic 1 0 2025-11-23 11:02:04.343
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.

!ENTRY org.eclipse.lemminx.uber-jar 4 0 2025-11-23 11:02:04.704
!MESSAGE bundle org.eclipse.lemminx.uber-jar:0.31.0 (312) Component descriptor entry 'OSGI-INF/*.xml' not found

!ENTRY org.eclipse.core.resources 2 10035 2025-11-23 11:02:08.875
!MESSAGE The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes.

!ENTRY ch.qos.logback.classic 1 0 2025-11-23 11:02:10.164
!MESSAGE Logback config file: C:\Users\xxxxx\.refactoring-workspace\.metadata\.plugins\org.eclipse.m2e.logback\logback.2.7.100.20250418-1315.xml

!ENTRY org.eclipse.osgi 4 0 2025-11-23 11:02:18.608
!MESSAGE Application error
!STACK 1
java.lang.RuntimeException: Error on /refactoring-cli-project/src_0/DecompilerPreferenceAction.java
    at org.eclipse.jdt.core.dom.CompilationUnitResolver.resolve(CompilationUnitResolver.java:999)
    at org.eclipse.jdt.core.dom.CompilationUnitResolver.resolve(CompilationUnitResolver.java:731)
    at org.eclipse.jdt.core.dom.CompilationUnitResolver$ECJCompilationUnitResolver.resolve(CompilationUnitResolver.java:103)
    at org.eclipse.jdt.core.dom.ASTParser.createASTs(ASTParser.java:1004)
    at org.eclipse.jdt.internal.corext.dom.ASTBatchParser.createASTs(ASTBatchParser.java:83)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring$CleanUpFixpointIterator.next(CleanUpRefactoring.java:410)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring.cleanUpProject(CleanUpRefactoring.java:706)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring.checkFinalConditions(CleanUpRefactoring.java:666)
    at io.github.xxxxxxxxx.refactoring.cli.CleanupRunner.run(CleanupRunner.java:174)
    at io.github.xxxxxxxxx.refactoring.cli.HeadlessCleanupApp.start(HeadlessCleanupApp.java:58)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:627)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:575)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1431)
Caused by: java.lang.IllegalStateException: Workbench has not been created yet.
    at org.eclipse.ui.PlatformUI.getWorkbench(PlatformUI.java:119)
    at org.eclipse.jdt.internal.corext.fix.ImportsFix.runUsingProgressService(ImportsFix.java:85)
    at org.eclipse.jdt.internal.corext.fix.ImportsFix.createCleanUp(ImportsFix.java:63)
    at org.eclipse.jdt.internal.ui.fix.ImportsCleanUp.createFix(ImportsCleanUp.java:62)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring.calculateChange(CleanUpRefactoring.java:796)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring$CleanUpASTRequestor.calculateSolutions(CleanUpRefactoring.java:303)
    at org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring$CleanUpASTRequestor.acceptAST(CleanUpRefactoring.java:281)
    at org.eclipse.jdt.core.dom.CompilationUnitResolver.resolve(CompilationUnitResolver.java:997)
    ... 19 more

!ENTRY org.eclipse.core.resources 2 10035 2025-11-23 11:02:19.594
!MESSAGE The workspace will exit with unsaved changes in this session.

The class that is being cleaned up is the following (it is part of a forked project ECD) :

package org.sf.feeling.decompiler.actions;

import org.eclipse.jface.action.Action;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.sf.feeling.decompiler.editor.JavaDecompilerClassFileEditor;
import org.sf.feeling.decompiler.i18n.Messages;
import org.sf.feeling.decompiler.util.UIUtil;

public class DecompilerPreferenceAction extends Action {

    public DecompilerPreferenceAction() {
        super(Messages.getString("JavaDecompilerActionBarContributor.Action.Preferences")); //$NON-NLS-1$
    }

    @Override
    public void run() {
        JavaDecompilerClassFileEditor editor = UIUtil.getActiveDecompilerEditor();

        String showId = "org.sf.feeling.decompiler.Main"; //$NON-NLS-1$

        if (editor != null) {
            PreferencesUtil.createPreferenceDialogOn(Display.getDefault().getActiveShell(), showId, // $NON-NLS-1$
                    editor.collectContextMenuPreferencePages(), null).open();
        } else {
            PreferencesUtil.createPreferenceDialogOn(Display.getDefault().getActiveShell(), showId, // $NON-NLS-1$
                    new String[] { showId // $NON-NLS-1$
                    }, null).open();
        }
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
2
  • The JDT code you are calling is calling PlatformUI.getWorkbench to get the IWorkbench. That only existing if the application calls PlatformUI.createAndRunWorkbench. You can't get the workbench in a headless app. Commented Nov 23 at 10:37
  • Understood. I will turn off the concerned rules. There's a bug in my code where cleanups don't apply to the version of the file edited by previous cleanup. It creates a mess with edits at the wrong locations. Commented Nov 23 at 13:16

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.