CodeCleanupApplicationWrapper.java
package org.sandbox.jdt.core.cleanupapp;
/*-
* #%L
* Sandbox cleanup application
* %%
* Copyright (C) 2024 hammer
* %%
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
* #L%
*/
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
public class CodeCleanupApplicationWrapper implements IApplication {
private static final String ARG_IMPORT_PROJECT= "--import-project"; //$NON-NLS-1$
private record ImportRequest(File projectDirectory, String[] applicationArguments) {
ImportRequest {
applicationArguments= applicationArguments.clone();
}
@Override
public String[] applicationArguments() {
return applicationArguments.clone();
}
}
private static final class ForwardingApplicationContext implements IApplicationContext {
private final IApplicationContext delegate;
private final String[] applicationArguments;
private final IApplication owner;
ForwardingApplicationContext(IApplicationContext delegate, String[] applicationArguments, IApplication owner) {
this.delegate= delegate;
this.applicationArguments= applicationArguments.clone();
this.owner= owner;
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map getArguments() {
Map forwarded= new HashMap(delegate.getArguments());
forwarded.put(APPLICATION_ARGS, applicationArguments.clone());
return forwarded;
}
@Override
public void applicationRunning() {
delegate.applicationRunning();
}
@Override
public String getBrandingApplication() {
return delegate.getBrandingApplication();
}
@Override
public Bundle getBrandingBundle() {
return delegate.getBrandingBundle();
}
@Override
public String getBrandingDescription() {
return delegate.getBrandingDescription();
}
@Override
public String getBrandingId() {
return delegate.getBrandingId();
}
@Override
public String getBrandingName() {
return delegate.getBrandingName();
}
@Override
public String getBrandingProperty(String key) {
return delegate.getBrandingProperty(key);
}
@Override
public void setResult(Object result, IApplication application) {
delegate.setResult(result, owner);
}
}
/**
* Deals with the messages in the properties file (cut n' pasted from a generated class).
*/
private static final class Messages extends NLS {
private static final String BUNDLE_NAME= "org.sandbox.jdt.core.cleanupapp.messages"; //$NON-NLS-1$
public static String CommandLineUsage;
public static String WorkspaceRequired;
static {
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
}
public static String bind(final String message) {
return bind(message, null);
}
public static String bind(final String message, final Object binding) {
return bind(message, new Object[] { binding });
}
public static String bind(final String message, final Object binding1, final Object binding2) {
return bind(message, new Object[] { binding1, binding2 });
}
public static String bind(final String message, final Object[] bindings) {
return MessageFormat.format(message, bindings);
}
}
private final CodeCleanupApplication delegate= new CodeCleanupApplication();
@Override
public Object start(final IApplicationContext context) throws NoClassDefFoundError, Exception {
final String[] arguments= (String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS);
final ImportRequest request;
try {
request= parseImportRequest(arguments);
} catch (IllegalArgumentException e) {
return fail(e.getMessage());
}
final List<String> args= Arrays.asList(request.applicationArguments());
if (args.isEmpty() || args.contains("-help") || args.contains("--help")) { //$NON-NLS-1$ //$NON-NLS-2$
System.out.println(Messages.bind(Messages.CommandLineUsage));
return IApplication.EXIT_OK;
}
try {
ResourcesPlugin.getWorkspace();
} catch (NoClassDefFoundError noClassError) {
if (noClassError.getCause() instanceof ClassNotFoundException classNotFoundException
&& classNotFoundException.getException() instanceof BundleException bundleException
&& bundleException.getCause() instanceof IllegalStateException) {
return fail(Messages.bind(Messages.WorkspaceRequired));
}
throw noClassError;
}
if (request.projectDirectory() != null) {
try {
importProject(request.projectDirectory());
} catch (CoreException | IOException e) {
return fail(e.getMessage());
}
}
Object result= delegate.start(new ForwardingApplicationContext(context, request.applicationArguments(), this));
return delegate.hasErrors() ? Integer.valueOf(CodeCleanupApplication.EXIT_ERROR) : result;
}
private static Integer fail(String message) {
System.err.println(failureMessage(message));
System.out.println(Messages.bind(Messages.CommandLineUsage));
return Integer.valueOf(CodeCleanupApplication.EXIT_ERROR);
}
static String failureMessage(String message) {
return message == null || message.isBlank()
? "Cleanup application failed without a diagnostic message." //$NON-NLS-1$
: message;
}
private static ImportRequest parseImportRequest(String[] arguments) {
if (arguments == null) {
throw new IllegalArgumentException("Application arguments are unavailable."); //$NON-NLS-1$
}
List<String> forwarded= new ArrayList<>(arguments.length);
File projectDirectory= null;
for (int index= 0; index < arguments.length; index++) {
String argument= arguments[index];
if (!ARG_IMPORT_PROJECT.equals(argument)) {
forwarded.add(argument);
continue;
}
if (projectDirectory != null) {
throw new IllegalArgumentException("The --import-project option may only be specified once."); //$NON-NLS-1$
}
if (++index >= arguments.length) {
throw new IllegalArgumentException("The --import-project option requires a project directory."); //$NON-NLS-1$
}
projectDirectory= new File(arguments[index]);
}
return new ImportRequest(projectDirectory, forwarded.toArray(String[]::new));
}
private static void importProject(File projectDirectory) throws CoreException, IOException {
File canonicalDirectory= projectDirectory.getCanonicalFile();
File projectDescriptionFile= new File(canonicalDirectory, ".project"); //$NON-NLS-1$
if (!canonicalDirectory.isDirectory() || !projectDescriptionFile.isFile()) {
throw new IOException("The imported project directory must contain a .project file: " //$NON-NLS-1$
+ canonicalDirectory);
}
IWorkspace workspace= ResourcesPlugin.getWorkspace();
IWorkspaceRoot root= workspace.getRoot();
IPath projectLocation= Path.fromOSString(canonicalDirectory.getAbsolutePath());
IProjectDescription description= workspace.loadProjectDescription(
Path.fromOSString(projectDescriptionFile.getAbsolutePath()));
IProject project= root.getProject(description.getName());
NullProgressMonitor monitor= new NullProgressMonitor();
if (!project.exists()) {
IPath workspaceLocation= root.getLocation();
IPath defaultLocation= workspaceLocation == null ? null : workspaceLocation.append(description.getName());
description.setLocation(projectLocation.equals(defaultLocation) ? null : projectLocation);
project.create(description, monitor);
} else if (project.getLocation() != null && !projectLocation.equals(project.getLocation())) {
throw new IOException("Workspace project already exists at another location: " + description.getName()); //$NON-NLS-1$
}
if (!project.isOpen()) {
project.open(monitor);
}
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
@Override
public void stop() {
delegate.stop();
}
}