blob: dcd7d3eea624df4b0776eb3105e5dcbb884f3345 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2011 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.jdt.internal.corext.fix;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import com.ibm.icu.text.Collator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
import org.eclipse.jdt.ui.cleanup.ICleanUp;
import org.eclipse.jdt.ui.cleanup.ICleanUpConfigurationUI;
import org.eclipse.jdt.ui.cleanup.ICleanUpOptionsInitializer;
import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions;
import org.eclipse.jdt.internal.ui.preferences.cleanup.CleanUpTabPage;
import org.eclipse.jdt.internal.ui.preferences.cleanup.ContributedCleanUpTabPage;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
/**
* The clean up registry provides a set of clean ups and there corresponding UI representatives.
*
* @since 3.4
*/
public class CleanUpRegistry {
private static final class ErrorPage implements ICleanUpConfigurationUI {
private final Exception fException;
private ErrorPage(Exception e) {
fException= e;
}
@Override
public Composite createContents(Composite parent) {
Composite result= new Composite(parent, SWT.NONE);
result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
result.setLayout(new GridLayout(1, false));
Text text= new Text(result, SWT.MULTI | SWT.BORDER | SWT.READ_ONLY);
SWTUtil.fixReadonlyTextBackground(text);
text.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
text.setText(Messages.format(FixMessages.CleanUpRegistry_ErrorTabPage_description, fException.getLocalizedMessage()));
return result;
}
@Override
public int getCleanUpCount() {
return 0;
}
@Override
public String getPreview() {
return FixMessages.CleanUpRegistry_ErrorTabPage_preview;
}
@Override
public int getSelectedCleanUpCount() {
return 0;
}
@Override
public void setOptions(CleanUpOptions options) {
}
}
public static class CleanUpTabPageDescriptor {
private static final String ATTRIBUTE_ID_CLASS= "class"; //$NON-NLS-1$
private static final String ATTRIBUTE_ID_NAME= "name"; //$NON-NLS-1$
private static final String ATTRIBUTE_NAME_KIND= "cleanUpKind"; //$NON-NLS-1$
private final String fName;
private final IConfigurationElement fElement;
private int fKind;
/**
* @param element the configuration element
*/
public CleanUpTabPageDescriptor(IConfigurationElement element) {
fElement= element;
fName= element.getAttribute(ATTRIBUTE_ID_NAME);
String kind= fElement.getAttribute(ATTRIBUTE_NAME_KIND);
fKind= getCleanUpKind(kind);
if (fKind == -1)
JavaPlugin.logErrorMessage(Messages.format(FixMessages.CleanUpRegistry_WrongKindForConfigurationUI_error, new String[] { fName, element.getContributor().getName(),
kind }));
}
/**
* @return the name of the tab
*/
public String getName() {
return fName;
}
/**
* @return the kind of clean up
*/
public int getKind() {
return fKind;
}
/**
* @return new instance of a tab page
*/
public CleanUpTabPage createTabPage() {
try {
ICleanUpConfigurationUI page= (ICleanUpConfigurationUI)fElement.createExecutableExtension(ATTRIBUTE_ID_CLASS);
if (page instanceof CleanUpTabPage)
return (CleanUpTabPage)page;
return new ContributedCleanUpTabPage(page);
} catch (final CoreException e) {
JavaPlugin.log(e);
return new ContributedCleanUpTabPage(new ErrorPage(e));
}
}
}
private static class CleanUpDescriptor {
private static final String ATTRIBUTE_ID_CLASS= "class"; //$NON-NLS-1$
private static final String ATTRIBURE_ID_RUNAFTER= "runAfter"; //$NON-NLS-1$
private static final String ATTRIBUTE_ID_ID= "id"; //$NON-NLS-1$
private final IConfigurationElement fElement;
private final String fId;
private final String fRunAfter;
/**
* @param element the configuration element
*/
public CleanUpDescriptor(IConfigurationElement element) {
fElement= element;
fId= element.getAttribute(ATTRIBUTE_ID_ID);
fRunAfter= element.getAttribute(ATTRIBURE_ID_RUNAFTER);
}
/**
* @return the unique id of this clean up
*/
public String getId() {
return fId;
}
/**
* @return the id of the clean up which must run before this clean up or
* <strong>null</strong> if none specified
*/
public String getRunAfter() {
return fRunAfter;
}
/**
* @return the clean up or <code>null</code> if the clean up could not be instantiated
*/
public ICleanUp createCleanUp() {
try {
return (ICleanUp)fElement.createExecutableExtension(ATTRIBUTE_ID_CLASS);
} catch (CoreException e) {
String msg= Messages.format(FixMessages.CleanUpRegistry_cleanUpCreation_error, new String[] { fElement.getAttribute(ATTRIBUTE_ID_ID), fElement.getContributor().getName() });
JavaPlugin.logErrorStatus(msg, e.getStatus());
return null;
}
}
}
private static final class CleanUpInitializerDescriptor {
private static final String ATTRIBUTE_NAME_CLASS= "class"; //$NON-NLS-1$
private static final String ATTRIBUTE_NAME_KIND= "cleanUpKind"; //$NON-NLS-1$
private final IConfigurationElement fElement;
private final int fKind;
private ICleanUpOptionsInitializer fOptionsProvider;
public CleanUpInitializerDescriptor(IConfigurationElement element) {
fElement= element;
String kind= fElement.getAttribute(ATTRIBUTE_NAME_KIND);
fKind= getCleanUpKind(kind);
if (fKind == -1)
JavaPlugin.logErrorMessage(Messages.format(FixMessages.CleanUpRegistry_UnknownInitializerKind_errorMessage, new String[] { element.getContributor().getName(), kind }));
}
public int getKind() {
return fKind;
}
public ICleanUpOptionsInitializer getOptionsInitializer() {
if (fOptionsProvider == null) {
try {
fOptionsProvider= (ICleanUpOptionsInitializer)fElement.createExecutableExtension(ATTRIBUTE_NAME_CLASS);
} catch (CoreException e) {
JavaPlugin.log(e);
fOptionsProvider= options -> {
};
}
}
return fOptionsProvider;
}
}
private static final String EXTENSION_POINT_NAME= "cleanUps"; //$NON-NLS-1$
private static final String CLEAN_UP_CONFIGURATION_ELEMENT_NAME= "cleanUp"; //$NON-NLS-1$
private static final String TABPAGE_CONFIGURATION_ELEMENT_NAME= "cleanUpConfigurationUI"; //$NON-NLS-1$
private static final String CLEAN_UP_INITIALIZER_CONFIGURATION_ELEMENT_NAME= "cleanUpOptionsInitializer"; //$NON-NLS-1$
private static final String ATTRIBUTE_KIND_TYPE_SAVE_ACTION= "saveAction"; //$NON-NLS-1$
private static final String ATTRIBUTE_KIND_TYPE_CLEAN_UP= "cleanUp"; //$NON-NLS-1$
private CleanUpDescriptor[] fCleanUpDescriptors;
private CleanUpTabPageDescriptor[] fPageDescriptors;
private CleanUpInitializerDescriptor[] fCleanUpInitializerDescriptors;
/**
* Creates and returns the registered clean ups that don't fail upon creation.
*
* @return an array of clean ups
*/
public synchronized ICleanUp[] createCleanUps() {
return createCleanUps(null);
}
/**
* Creates and returns the registered clean ups that don't fail upon creation.
*
* @param ids the ids of the clean ups to create, or <code>null</code> to create all
* @return an array of clean ups
* @since 3.5
*/
public synchronized ICleanUp[] createCleanUps(Set<String> ids) {
ensureCleanUpsRegistered();
ArrayList<ICleanUp> result= new ArrayList<>(fCleanUpDescriptors.length);
for (CleanUpDescriptor descriptor : fCleanUpDescriptors) {
if (ids == null || ids.contains(descriptor.getId())) {
ICleanUp cleanUp= descriptor.createCleanUp();
if (cleanUp != null)
result.add(cleanUp);
}
}
return result.toArray(new ICleanUp[result.size()]);
}
/**
* @param kind the kind of clean up for which to retrieve the configuratin pages
*
* @return set of clean up tab page descriptors
*
* @see CleanUpConstants#DEFAULT_CLEAN_UP_OPTIONS
* @see CleanUpConstants#DEFAULT_SAVE_ACTION_OPTIONS
*/
public synchronized CleanUpTabPageDescriptor[] getCleanUpTabPageDescriptors(int kind) {
ensurePagesRegistered();
ArrayList<CleanUpTabPageDescriptor> result= new ArrayList<>();
for (CleanUpTabPageDescriptor descriptor : fPageDescriptors) {
if (descriptor.getKind() == kind) {
result.add(descriptor);
}
}
return result.toArray(new CleanUpTabPageDescriptor[result.size()]);
}
/**
* Returns the default options for the specified clean up kind.
*
* @param kind the kind of clean up for which to retrieve the options
* @return the default options
*
* @see CleanUpConstants#DEFAULT_CLEAN_UP_OPTIONS
* @see CleanUpConstants#DEFAULT_SAVE_ACTION_OPTIONS
*/
public MapCleanUpOptions getDefaultOptions(int kind) {
ensureCleanUpInitializersRegistered();
CleanUpOptions options= new CleanUpOptions();
for (CleanUpInitializerDescriptor descriptor : fCleanUpInitializerDescriptors) {
if (descriptor.getKind() == kind) {
descriptor.getOptionsInitializer().setDefaultOptions(options);
}
}
MapCleanUpOptions mapCleanUpOptions= new MapCleanUpOptions();
mapCleanUpOptions.addAll(options);
return mapCleanUpOptions;
}
private synchronized void ensureCleanUpsRegistered() {
if (fCleanUpDescriptors != null)
return;
final ArrayList<CleanUpDescriptor> descriptors= new ArrayList<>();
IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
for (IConfigurationElement element : point.getConfigurationElements()) {
if (CLEAN_UP_CONFIGURATION_ELEMENT_NAME.equals(element.getName())) {
descriptors.add(new CleanUpDescriptor(element));
}
}
// Make sure we filter those who fail or misbehave
for (int i= 0; i < descriptors.size(); i++) {
final CleanUpDescriptor cleanUpDescriptor= descriptors.get(i);
final boolean disable[]= new boolean[1];
ISafeRunnable runnable= new SafeRunnable() {
@Override
public void run() throws Exception {
ICleanUp cleanUp= cleanUpDescriptor.createCleanUp();
if (cleanUp == null)
disable[0]= true;
else {
cleanUp.setOptions(new CleanUpOptions());
String[] enbledSteps= cleanUp.getStepDescriptions();
if (enbledSteps != null && enbledSteps.length > 0) {
JavaPlugin.logErrorMessage(
Messages.format(FixMessages.CleanUpRegistry_cleanUpAlwaysEnabled_error, new String[] { cleanUpDescriptor.getId(),
cleanUpDescriptor.fElement.getContributor().getName() }));
disable[0]= true;
}
}
}
@Override
public void handleException(Throwable t) {
disable[0]= true;
String message= Messages.format(FixMessages.CleanUpRegistry_cleanUpCreation_error, new String[] { cleanUpDescriptor.getId(),
cleanUpDescriptor.fElement.getContributor().getName() });
IStatus status= new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, t);
JavaPlugin.log(status);
}
};
SafeRunner.run(runnable);
if (disable[0])
descriptors.remove(i--);
}
fCleanUpDescriptors= descriptors.toArray(new CleanUpDescriptor[descriptors.size()]);
sort(fCleanUpDescriptors);
}
private static void sort(CleanUpDescriptor[] data) {
int lastSwapI= -1;
int lastSwapJ= -1;
mainLoop: for (int i= 0; i < data.length; i++) {
String runAfter= data[i].getRunAfter();
if (runAfter == null)
continue;
int jStart= i + 1;
for (int j= jStart; j < data.length; j++) {
String jID= data[j].getId();
if (runAfter.equals(jID)) {
if (lastSwapI == i && j >= lastSwapJ) {
JavaPlugin.logErrorMessage("Problem reading cleanUps extensions: cannot satisfy rule for '" + data[i].getId() + "' to runAfter '" + runAfter + "'"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
continue mainLoop;
}
lastSwapI= i;
lastSwapJ= j;
swap(data, i, j);
i--;
continue mainLoop;
}
}
for (int j= 0; j < jStart; j++) {
String jID= data[j].getId();
if (runAfter.equals(jID))
continue mainLoop;
}
JavaPlugin.logErrorMessage("Problem reading cleanUps extensions: cannot satisfy rule for '" + data[i].getId() + "' to runAfter '" + runAfter + "' because the runAfter clean up does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
private static void swap(CleanUpDescriptor[] data, int i, int j) {
CleanUpDescriptor o= data[i];
data[i]= data[j];
data[j]= o;
}
private synchronized void ensurePagesRegistered() {
if (fPageDescriptors != null)
return;
ArrayList<CleanUpTabPageDescriptor> result= new ArrayList<>();
IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
for (IConfigurationElement element : point.getConfigurationElements()) {
if (TABPAGE_CONFIGURATION_ELEMENT_NAME.equals(element.getName())) {
result.add(new CleanUpTabPageDescriptor(element));
}
}
fPageDescriptors= result.toArray(new CleanUpTabPageDescriptor[result.size()]);
Arrays.sort(fPageDescriptors, (o1, o2) -> {
String name1= o1.getName();
String name2= o2.getName();
return Collator.getInstance().compare(name1.replaceAll("&", ""), name2.replaceAll("&", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
});
}
private synchronized void ensureCleanUpInitializersRegistered() {
if (fCleanUpInitializerDescriptors != null)
return;
ArrayList<CleanUpInitializerDescriptor> result= new ArrayList<>();
IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
for (IConfigurationElement element : point.getConfigurationElements()) {
if (CLEAN_UP_INITIALIZER_CONFIGURATION_ELEMENT_NAME.equals(element.getName())) {
result.add(new CleanUpInitializerDescriptor(element));
}
}
fCleanUpInitializerDescriptors= result.toArray(new CleanUpInitializerDescriptor[result.size()]);
}
private static int getCleanUpKind(String kind) {
switch (kind) {
case ATTRIBUTE_KIND_TYPE_CLEAN_UP:
return CleanUpConstants.DEFAULT_CLEAN_UP_OPTIONS;
case ATTRIBUTE_KIND_TYPE_SAVE_ACTION:
return CleanUpConstants.DEFAULT_SAVE_ACTION_OPTIONS;
default:
return -1;
}
}
}