--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * 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.Comparator;
+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;
+
+/**
+ * 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;
+ }
+
+ 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);
+ text.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ text.setText(Messages.format(FixMessages.CleanUpRegistry_ErrorTabPage_description, fException.getLocalizedMessage()));
+
+ return result;
+ }
+
+ public int getCleanUpCount() {
+ return 0;
+ }
+
+ public String getPreview() {
+ return FixMessages.CleanUpRegistry_ErrorTabPage_preview;
+ }
+
+ public int getSelectedCleanUpCount() {
+ return 0;
+ }
+
+ 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= new ICleanUpOptionsInitializer() {
+ public void setDefaultOptions(CleanUpOptions 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<ICleanUp>(fCleanUpDescriptors.length);
+ for (int i= 0; i < fCleanUpDescriptors.length; i++) {
+ if (ids == null || ids.contains(fCleanUpDescriptors[i].getId())) {
+ ICleanUp cleanUp= fCleanUpDescriptors[i].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<CleanUpTabPageDescriptor>();
+ for (int i= 0; i < fPageDescriptors.length; i++) {
+ if (fPageDescriptors[i].getKind() == kind)
+ result.add(fPageDescriptors[i]);
+ }
+ 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 (int i= 0; i < fCleanUpInitializerDescriptors.length; i++) {
+ CleanUpInitializerDescriptor descriptor= fCleanUpInitializerDescriptors[i];
+ 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<CleanUpDescriptor>();
+
+ IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
+ IConfigurationElement[] elements= point.getConfigurationElements();
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+
+ 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() {
+
+ 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<CleanUpTabPageDescriptor>();
+
+ IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
+ IConfigurationElement[] elements= point.getConfigurationElements();
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+
+ if (TABPAGE_CONFIGURATION_ELEMENT_NAME.equals(element.getName())) {
+ result.add(new CleanUpTabPageDescriptor(element));
+ }
+ }
+
+ fPageDescriptors= result.toArray(new CleanUpTabPageDescriptor[result.size()]);
+ Arrays.sort(fPageDescriptors, new Comparator<CleanUpTabPageDescriptor>() {
+ public int compare(CleanUpTabPageDescriptor o1, CleanUpTabPageDescriptor 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<CleanUpInitializerDescriptor>();
+
+ IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(JavaPlugin.getPluginId(), EXTENSION_POINT_NAME);
+ IConfigurationElement[] elements= point.getConfigurationElements();
+ for (int i= 0; i < elements.length; i++) {
+ IConfigurationElement element= elements[i];
+
+ 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) {
+ if (kind.equals(ATTRIBUTE_KIND_TYPE_CLEAN_UP)) {
+ return CleanUpConstants.DEFAULT_CLEAN_UP_OPTIONS;
+ } else if (kind.equals(ATTRIBUTE_KIND_TYPE_SAVE_ACTION)) {
+ return CleanUpConstants.DEFAULT_SAVE_ACTION_OPTIONS;
+ } else {
+ return -1;
+ }
+ }
+
+}