/*******************************************************************************
 * Copyright (c) 2005, 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
 *     Sergey Prigogin <eclipse.sprigogin@gmail.com> - [refactoring] Provide a way to implement refactorings that depend on resources that have to be explicitly released - https://bugs.eclipse.org/347599
 *******************************************************************************/
package org.eclipse.ltk.ui.refactoring.history;

import java.lang.reflect.InvocationTargetException;
import java.text.ChoiceFormat;
import java.util.ArrayList;
import java.util.List;

import com.ibm.icu.text.MessageFormat;

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.Shell;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;

import org.eclipse.core.resources.ResourcesPlugin;

import org.eclipse.jface.action.LegacyActionTools;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;

import org.eclipse.ui.PlatformUI;

import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CheckConditionsOperation;
import org.eclipse.ltk.core.refactoring.CreateChangeOperation;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.PerformRefactoringHistoryOperation;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringContext;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
import org.eclipse.ltk.core.refactoring.history.IRefactoringHistoryService;
import org.eclipse.ltk.core.refactoring.history.RefactoringHistory;
import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryImplementation;
import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryManager;
import org.eclipse.ltk.internal.ui.refactoring.ChangeExceptionHandler;
import org.eclipse.ltk.internal.ui.refactoring.ExceptionHandler;
import org.eclipse.ltk.internal.ui.refactoring.IErrorWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.IPreviewWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.IRefactoringHelpContextIds;
import org.eclipse.ltk.internal.ui.refactoring.Messages;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringHistoryPreviewPage;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringPluginImages;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringPreviewChangeFilter;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringStatusEntryFilter;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIMessages;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIPlugin;
import org.eclipse.ltk.internal.ui.refactoring.UIPerformChangeOperation;
import org.eclipse.ltk.internal.ui.refactoring.WorkbenchRunnableAdapter;
import org.eclipse.ltk.internal.ui.refactoring.history.RefactoringHistoryErrorPage;
import org.eclipse.ltk.internal.ui.refactoring.history.RefactoringHistoryOverviewPage;

/**
 * A default implementation of a refactoring history wizard. Refactoring history
 * wizards are used to execute the refactorings described by a refactoring
 * history. A refactoring history wizard differs from a normal wizard in the
 * following characteristics:
 * <ul>
 * <li>A refactoring history wizard consists of a sequence of one error page to
 * present the outcome of a refactoring's condition checking and one preview
 * page to present a preview of the workspace changes.</li>
 * <li> Refactorings are applied to the workspace as soon as a preview has been
 * accepted. Additionally, refactoring history wizards support the headless
 * execution of refactorings. The user guided execution of a refactoring history
 * triggers a series of error pages and preview pages. Within this sequence of
 * pages, going back is not supported anymore. However, canceling the
 * refactoring history wizard will undo the already performed refactorings.</li>
 * </ul>
 * <p>
 * A refactoring history wizard is usually opened using the {@link WizardDialog}.
 * Clients must ensure that the calling thread holds the workspace lock.
 * </p>
 * <p>
 * Note: this class is intended to be extended by clients.
 * </p>
 *
 * @see org.eclipse.ltk.core.refactoring.Refactoring
 * @see org.eclipse.ltk.core.refactoring.history.RefactoringHistory
 *
 * @since 3.2
 */
public class RefactoringHistoryWizard extends Wizard {

	/** The no overview wizard page */
	private final class NoOverviewWizardPage extends WizardPage {

		/** The no overview wizard page name */
		private static final String PAGE_NAME= "NoOverviewWizardPage"; //$NON-NLS-1$

		/**
		 * Creates a new no overview wizard page.
		 */
		private NoOverviewWizardPage() {
			super(PAGE_NAME);
			final RefactoringDescriptorProxy[] proxies= getRefactoringDescriptors();
			if (proxies.length > 0)
				setTitle(proxies[0], 1, proxies.length);
			else
				setTitle(fOverviewTitle);
			setDescription(RefactoringUIMessages.RefactoringHistoryPreviewPage_description);
		}

		/**
		 * {@inheritDoc}
		 */
		public boolean canFlipToNextPage() {
			return true;
		}

		/**
		 * {@inheritDoc}
		 */
		public void createControl(final Composite parent) {
			final Composite composite= new Composite(parent, SWT.NULL);
			composite.setLayout(new GridLayout());
			composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
			setControl(composite);
			Dialog.applyDialogFont(composite);
			PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IRefactoringHelpContextIds.REFACTORING_PREVIEW_WIZARD_PAGE);
		}

		/**
		 * {@inheritDoc}
		 */
		public IWizardPage getNextPage() {
			return getWizard().getNextPage(this);
		}

		/**
		 * {@inheritDoc}
		 */
		public IWizardPage getPreviousPage() {
			return getWizard().getPreviousPage(this);
		}

		/**
		 * {@inheritDoc}
		 */
		public void setPageComplete(final boolean complete) {
			super.setPageComplete(true);
		}

		/**
		 * Sets the title of the page according to the refactoring.
		 *
		 * @param descriptor
		 *            the refactoring descriptor, or <code>null</code>
		 * @param current
		 *            the non-zero based index of the current refactoring
		 * @param total
		 *            the total number of refactorings
		 */
		public void setTitle(final RefactoringDescriptorProxy descriptor, final int current, final int total) {
			final String message;
			if (descriptor != null)
				message= descriptor.getDescription();
			else
				message= RefactoringUIMessages.RefactoringHistoryOverviewPage_title;
			if (total > 1)
				setTitle(Messages.format(RefactoringUIMessages.RefactoringHistoryPreviewPage_refactoring_pattern, new String[] { message, String.valueOf(current + 1), String.valueOf(total)}));
			else
				setTitle(message);
		}
	}

	/** Preference key for the show apply preference */
	private static final String PREFERENCE_DO_NOT_SHOW_APPLY_ERROR= RefactoringUIPlugin.getPluginId() + ".do.not.show.apply.refactoring"; //$NON-NLS-1$;

	/** Preference key for the show skip preference */
	private static final String PREFERENCE_DO_NOT_SHOW_SKIP= RefactoringUIPlugin.getPluginId() + ".do.not.show.skip.refactoring"; //$NON-NLS-1$

	/** Preference key for the warn finish preference */
	private static final String PREFERENCE_DO_NOT_WARN_FINISH= RefactoringUIPlugin.getPluginId() + ".do.not.warn.finish.wizard"; //$NON-NLS-1$;

	/** Preference key for the warn undo on cancel preference */
	private static final String PREFERENCE_DO_NOT_WARN_UNDO_ON_CANCEL= RefactoringUIPlugin.getPluginId() + ".do.not.warn.undo.on.cancel.refactoring"; //$NON-NLS-1$;

	/**
	 * The status code representing an interrupted operation.
	 * <p>
	 * Note: This API must not be used from outside the refactoring framework.
	 * </p>
	 */
	public static final int STATUS_CODE_INTERRUPTED= 10003;


	/** Has the about to perform history event already been fired? */
	private boolean fAboutToPerformFired= false;

	/** Has an exception occurred while cancelling the wizard? */
	private boolean fCancelException= false;

	/** The refactoring history control configuration to use */
	private RefactoringHistoryControlConfiguration fControlConfiguration;

	/** The index of the currently executed refactoring */
	private int fCurrentRefactoring= 0;

	/**
	 * The refactoring descriptor proxies, in ascending order of their time
	 * stamps, or <code>null</code>
	 */
	private RefactoringDescriptorProxy[] fDescriptorProxies= null;

	/** The error wizard page */
	private final RefactoringHistoryErrorPage fErrorPage;

	/** The number of successfully executed refactorings */
	private int fExecutedRefactorings= 0;

	/** Can the wizard be finished after fatal errors occurred in headless mode? */
	private boolean fHeadlessErrorStatus= false;

	/** Are we currently in method <code>addPages</code>? */
	private boolean fInAddPages= false;

	/**
	 * The no overview wizard page, or <code>null</code> if an overview is
	 * desired
	 */
	private NoOverviewWizardPage fNoOverviewPage;

	/** The description of the overview page */
	private final String fOverviewDescription;

	/**
	 * The overview wizard page, or <code>null</code> if no overview is
	 * desired
	 */
	private RefactoringHistoryOverviewPage fOverviewPage;

	/** The title of the overview page */
	private final String fOverviewTitle;

	/** The preview change filter */
	private RefactoringPreviewChangeFilter fPreviewChangeFilter= new RefactoringPreviewChangeFilter() {

		/**
		 * {@inheritDoc}
		 */
		public final boolean select(final Change change) {
			return selectPreviewChange(change);
		}
	};

	/** The preview wizard page */
	private final RefactoringHistoryPreviewPage fPreviewPage;

	/** The refactoring history to execute */
	private RefactoringHistory fRefactoringHistory;

	/** Does the wizard show an overview of the refactorings? */
	private final boolean fShowOverview;

	/** The status entry filter */
	private RefactoringStatusEntryFilter fStatusEntryFilter= new RefactoringStatusEntryFilter() {

		/**
		 * {@inheritDoc}
		 */
		public final boolean select(final RefactoringStatusEntry entry) {
			return selectStatusEntry(entry);
		}
	};

	/**
	 * Creates a new refactoring history wizard.
	 * <p>
	 * Clients must ensure that the refactoring history and the refactoring
	 * history control configuration are set before opening the wizard in a
	 * dialog.
	 * </p>
	 *
	 * @param overview
	 *            <code>true</code> to show an overview of the refactorings,
	 *            <code>false</code> otherwise
	 * @param caption
	 *            the caption of the wizard window
	 * @param title
	 *            the title of the overview page
	 * @param description
	 *            the description of the overview page
	 *
	 * @see #setConfiguration(RefactoringHistoryControlConfiguration)
	 * @see #setInput(RefactoringHistory)
	 */
	public RefactoringHistoryWizard(final boolean overview, final String caption, final String title, final String description) {
		Assert.isNotNull(caption);
		Assert.isNotNull(title);
		Assert.isNotNull(description);
		fShowOverview= overview;
		fOverviewTitle= title;
		fOverviewDescription= description;
		fErrorPage= new RefactoringHistoryErrorPage();
		fErrorPage.setFilter(fStatusEntryFilter);
		fPreviewPage= new RefactoringHistoryPreviewPage();
		fPreviewPage.setFilter(fPreviewChangeFilter);
		setNeedsProgressMonitor(true);
		setWindowTitle(caption);
		setDefaultPageImageDescriptor(RefactoringPluginImages.DESC_WIZBAN_REFACTOR);
	}

	/**
	 * Creates a new refactoring history wizard.
	 * <p>
	 * Clients must ensure that the refactoring history and the refactoring
	 * history control configuration are set before opening the wizard in a
	 * dialog.
	 * </p>
	 * <p>
	 * Calling his constructor is equivalent to
	 * {@link #RefactoringHistoryWizard(boolean, String, String, String)} with
	 * the first argument equal to <code>true</code>.
	 * </p>
	 *
	 * @param caption
	 *            the caption of the wizard window
	 * @param title
	 *            the title of the overview page
	 * @param description
	 *            the description of the overview page
	 *
	 * @see #setConfiguration(RefactoringHistoryControlConfiguration)
	 * @see #setInput(RefactoringHistory)
	 */
	public RefactoringHistoryWizard(final String caption, final String title, final String description) {
		this(true, caption, title, description);
	}

	/**
	 * Hook method which is called before the first refactoring of the history
	 * is executed. This method may be called from non-UI threads.
	 * <p>
	 * This method is guaranteed to be called exactly once during the lifetime
	 * of a refactoring history wizard. The default implementation does nothing
	 * and returns a refactoring status of severity {@link RefactoringStatus#OK}.
	 * </p>
	 * <p>
	 * Subclasses may reimplement this method to perform any special processing.
	 * </p>
	 * <p>
	 * Returning a status of severity {@link RefactoringStatus#FATAL} will
	 * terminate the execution of the refactorings.
	 * </p>
	 *
	 * @param monitor
	 *            the progress monitor to use
	 *
	 * @return a status describing the outcome of the operation
	 */
	protected RefactoringStatus aboutToPerformHistory(final IProgressMonitor monitor) {
		Assert.isNotNull(monitor);
		fExecutedRefactorings= 0;
		return new RefactoringStatus();
	}

	/**
	 * Hook method which is called before the a refactoring of the history is
	 * executed. The refactoring itself is in an initialized state at the time
	 * of the method call. The default implementation does nothing and returns a
	 * status of severity {@link RefactoringStatus#OK}. This method may be
	 * called from non-UI threads.
	 * <p>
	 * Subclasses may extend this method to perform any special processing.
	 * </p>
	 * <p>
	 * Returning a status of severity {@link RefactoringStatus#FATAL} will
	 * terminate the execution of the current refactoring.
	 * </p>
	 *
	 * @param refactoring
	 *            the refactoring about to be executed
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param monitor
	 *            the progress monitor to use
	 * @return a status describing the outcome of the initialization
	 */
	protected RefactoringStatus aboutToPerformRefactoring(final Refactoring refactoring, final RefactoringDescriptor descriptor, final IProgressMonitor monitor) {
		Assert.isNotNull(refactoring);
		Assert.isNotNull(descriptor);
		return new RefactoringStatus();
	}

	/**
	 * {@inheritDoc}
	 *
	 * Clients must contribute their wizard pages by re-implementing
	 * {@link #addUserDefinedPages()}.
	 */
	public final void addPage(final IWizardPage page) {
		Assert.isTrue(fInAddPages);
		super.addPage(page);
	}

	/**
	 * {@inheritDoc}
	 */
	public final void addPages() {
		try {
			fInAddPages= true;
			addUserDefinedPages();
			Assert.isNotNull(fRefactoringHistory);
			Assert.isNotNull(fControlConfiguration);
			if (fShowOverview) {
				fOverviewPage= new RefactoringHistoryOverviewPage(fRefactoringHistory, fOverviewTitle, fOverviewDescription, fControlConfiguration);
				addPage(fOverviewPage);
			} else {
				fNoOverviewPage= new NoOverviewWizardPage();
				addPage(fNoOverviewPage);
			}
			addPage(fErrorPage);
			addPage(fPreviewPage);
		} finally {
			fInAddPages= false;
		}
	}

	/**
	 * Adds user defined wizard pages in front of the wizard.
	 * <p>
	 * Clients may extend this method to add custom wizard pages in front of the
	 * wizard.
	 * </p>
	 */
	protected void addUserDefinedPages() {
		Assert.isTrue(fInAddPages);

		// Do not add any as default
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean canFinish() {
		final IWizardPage page= getContainer().getCurrentPage();
		if (page == fErrorPage) {
			if (fHeadlessErrorStatus)
				return true;
			final RefactoringStatus status= fErrorPage.getStatus();
			final boolean fatal= status != null && status.hasFatalError();
			if (isLastRefactoring() && fDescriptorProxies.length > 1) {
				if (fatal)
					fHeadlessErrorStatus= true;
				return true;
			}
			return !fatal;
		}
		return true;
	}

	/**
	 * Checks the specified kind of conditions of the refactoring.
	 *
	 * @param refactoring
	 *            the refactoring to check its conditions
	 * @param monitor
	 *            the progress monitor to use
	 * @param style
	 *            the condition checking style
	 * @throws OperationCanceledException
	 *             if the operation has been cancelled
	 * @return the resulting status
	 */
	private RefactoringStatus checkConditions(final Refactoring refactoring, final IProgressMonitor monitor, final int style) throws OperationCanceledException {
		Assert.isNotNull(refactoring);
		Assert.isNotNull(monitor);
		final RefactoringStatus status= new RefactoringStatus();
		try {
			final CheckConditionsOperation operation= new CheckConditionsOperation(refactoring, style);
			operation.run(monitor);
			status.merge(operation.getStatus());
		} catch (CoreException exception) {
			RefactoringUIPlugin.log(exception);
			status.addFatalError(RefactoringUIMessages.RefactoringWizard_internal_error_1);
		}
		return status;
	}

	/**
	 * Creates the change for the specified refactoring.
	 *
	 * @param refactoring
	 *            the refactoring
	 * @param monitor
	 *            the progress monitor
	 * @return the created change
	 * @throws OperationCanceledException
	 *             if the operation has been cancelled
	 * @throws CoreException
	 *             if an error occurs while creating the change
	 */
	private Change createChange(final Refactoring refactoring, final IProgressMonitor monitor) throws OperationCanceledException, CoreException {
		Assert.isNotNull(refactoring);
		Assert.isNotNull(monitor);
		final CreateChangeOperation operation= new CreateChangeOperation(refactoring);
		operation.run(monitor);
		return operation.getChange();
	}

	/**
	 * Method which is called to create a refactoring instance from a
	 * refactoring descriptor. The refactoring must be in an initialized state
	 * after the return of the method call. The default implementation delegates
	 * the task to the refactoring descriptor. This method may be called from
	 * non-UI threads.
	 * <p>
	 * Subclasses may reimplement this method to customize the initialization of
	 * a refactoring.
	 * </p>
	 *
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param status
	 *            a refactoring status describing the outcome of the
	 *            initialization
	 * @return the refactoring, or <code>null</code> if this refactoring
	 *         descriptor represents the unknown refactoring, or if no
	 *         refactoring contribution is available for this refactoring
	 *         descriptor
	 * @throws CoreException
	 *             if an error occurs while creating the refactoring instance
	 * @deprecated since 3.6. Override {@link #createRefactoringContext(RefactoringDescriptor, RefactoringStatus, IProgressMonitor)} instead
	 */
	protected Refactoring createRefactoring(final RefactoringDescriptor descriptor, final RefactoringStatus status) throws CoreException {
		Assert.isNotNull(descriptor);
		return descriptor.createRefactoring(status);
	}

	/**
	 * Creates a refactoring from the specified refactoring descriptor.
	 * <p>
	 * The default implementation calls {@link #createRefactoring(RefactoringDescriptor, RefactoringStatus)} followed by
	 * {@link #aboutToPerformRefactoring(Refactoring, RefactoringDescriptor, IProgressMonitor)}.
	 * Implementors can replace this implementation.
	 * </p>
	 *
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param status
	 *            the refactoring status
	 * @param monitor
	 *            the progress monitor to use
	 * @return the refactoring, or <code>null</code> if this refactoring
	 *         descriptor represents the unknown refactoring, or if no
	 *         refactoring contribution is available for this refactoring
	 *         descriptor
	 * @throws CoreException
	 *             if an error occurs while creating the refactoring instance
	 *
	 * @since 3.4
	 * @deprecated since 3.7. Override {@link #createRefactoringContext(RefactoringDescriptor, RefactoringStatus, IProgressMonitor)} instead
	 */
	protected Refactoring createRefactoring(final RefactoringDescriptor descriptor, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
		final Refactoring refactoring= createRefactoring(descriptor, status);
		if (refactoring != null) {
			status.merge(aboutToPerformRefactoring(refactoring, descriptor, monitor));
			if (!status.hasFatalError())
				return refactoring;
		} else
			status.addFatalError(Messages.format(RefactoringUIMessages.RefactoringHistoryWizard_error_instantiate_refactoring, descriptor.getDescription()));
		return null;
	}

	/**
	 * Creates a refactoring context from the specified refactoring descriptor.
	 * <p>
	 * The default implementation calls
	 * {@link RefactoringDescriptor#createRefactoringContext(RefactoringStatus)} followed by
	 * {@link #aboutToPerformRefactoring(Refactoring, RefactoringDescriptor, IProgressMonitor)}.
	 * Implementors can replace this implementation.
	 * </p>
	 *
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param status
	 *            the refactoring status
	 * @param monitor
	 *            the progress monitor to use
	 * @return the refactoring context, or <code>null</code> if this refactoring descriptor
	 *         represents the unknown refactoring, or if no refactoring contribution is available
	 *         for this refactoring descriptor
	 * @throws CoreException
	 *             if an error occurs while creating the refactoring context
	 *
	 * @since 3.7
	 */
	protected RefactoringContext createRefactoringContext(final RefactoringDescriptor descriptor, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
		Assert.isNotNull(descriptor);
		final RefactoringContext context= descriptor.createRefactoringContext(status);
		if (context != null) {
			final Refactoring refactoring= context.getRefactoring();
			status.merge(aboutToPerformRefactoring(refactoring, descriptor, monitor));
			if (!status.hasFatalError())
				return context;
		} else {
			status.addFatalError(Messages.format(RefactoringUIMessages.RefactoringHistoryWizard_error_instantiate_refactoring, descriptor.getDescription()));
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public void dispose() {
		SafeRunner.run(new ISafeRunnable() {

			public void handleException(final Throwable exception) {
				RefactoringUIPlugin.log(exception);
			}

			public final void run() throws Exception {
				if (fAboutToPerformFired) {
					final RefactoringStatusEntry entry= historyPerformed(new NullProgressMonitor()).getEntryWithHighestSeverity();
					if (entry != null)
						RefactoringUIPlugin.log(entry.toStatus());
				}
			}
		});
		super.dispose();
	}

	/**
	 * Fires the about to perform history event.
	 *
	 * @param monitor
	 *            the progress monitor to use
	 *
	 * @return a status describing the outcome of the operation
	 */
	private RefactoringStatus fireAboutToPerformHistory(final IProgressMonitor monitor) {
		final RefactoringStatus status= new RefactoringStatus();
		SafeRunner.run(new ISafeRunnable() {

			public void handleException(final Throwable exception) {
				RefactoringUIPlugin.log(exception);
				status.addFatalError(RefactoringUIMessages.RefactoringWizard_unexpected_exception_1);
			}

			public final void run() throws Exception {
				status.merge(aboutToPerformHistory(monitor));
			}
		});
		return status;
	}

	/**
	 * Returns the error wizard page.
	 * <p>
	 * Note: This API must not be called from outside the refactoring framework.
	 * </p>
	 *
	 * @return the error wizard page
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final IErrorWizardPage getErrorPage() {
		return fErrorPage;
	}

	/**
	 * {@inheritDoc}
	 */
	public IWizardPage getNextPage(final IWizardPage page) {
		if (page == fOverviewPage || page == fNoOverviewPage) {
			fCurrentRefactoring= 0;
			return getRefactoringPage();
		} else if (page == fPreviewPage) {
			fCurrentRefactoring++;
			return getRefactoringPage();
		} else if (page == fErrorPage) {
			final RefactoringStatus status= fErrorPage.getStatus();
			final IWizardContainer wizard= getContainer();
			if (status.hasFatalError()) {
				final IPreferenceStore store= RefactoringUIPlugin.getDefault().getPreferenceStore();
				String message= null;
				String key= null;
				if (!RefactoringUIMessages.RefactoringHistoryPreviewPage_apply_error_title.equals(fErrorPage.getTitle())) {
					message= Messages.format(RefactoringUIMessages.RefactoringHistoryWizard_fatal_error_message, fErrorPage.getTitle());
					key= PREFERENCE_DO_NOT_SHOW_SKIP;
				} else {
					message= RefactoringUIMessages.RefactoringHistoryWizard_error_applying_changes;
					key= PREFERENCE_DO_NOT_SHOW_APPLY_ERROR;
				}
				if (!store.getBoolean(key)) {
					final MessageDialogWithToggle dialog= new MessageDialogWithToggle(getShell(), wizard.getShell().getText(), null, message, MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0, RefactoringUIMessages.RefactoringHistoryWizard_do_not_show_message, false);
					dialog.open();
					store.setValue(key, dialog.getToggleState());
					if (dialog.getReturnCode() == 1)
						return null;
				}
				fCurrentRefactoring++;
				return getRefactoringPage();
			}
			final Refactoring refactoring= fErrorPage.getRefactoring();
			if (refactoring != null) {
				final IRunnableWithProgress runnable= new IRunnableWithProgress() {

					public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
						Assert.isNotNull(monitor);
						try {
							fPreviewPage.setRefactoring(refactoring);
							final Change change= createChange(refactoring, monitor);
							getShell().getDisplay().syncExec(new Runnable() {

								public final void run() {
									fPreviewPage.setChange(change);
								}
							});
						} catch (CoreException exception) {
							throw new InvocationTargetException(exception);
						} catch (OperationCanceledException exception) {
							throw new InterruptedException(exception.getLocalizedMessage());
						} finally {
							monitor.done();
						}
					}
				};
				try {
					wizard.run(true, false, runnable);
				} catch (InvocationTargetException exception) {
					final Throwable throwable= exception.getTargetException();
					if (throwable != null) {
						RefactoringUIPlugin.log(exception);
						fErrorPage.setStatus(RefactoringStatus.createFatalErrorStatus(RefactoringUIMessages.RefactoringWizard_unexpected_exception_1));
						return fErrorPage;
					}
				} catch (InterruptedException exception) {
					return fErrorPage;
				}
			} else {
				fPreviewPage.setRefactoring(null);
				fPreviewPage.setChange(null);
			}
			final RefactoringDescriptorProxy descriptor= getRefactoringDescriptor();
			if (descriptor != null)
				fPreviewPage.setTitle(descriptor, fCurrentRefactoring, fDescriptorProxies.length);
			else
				fPreviewPage.setTitle(RefactoringUIMessages.PreviewWizardPage_changes);
			fPreviewPage.setStatus(status);
			fPreviewPage.setNextPageDisabled(isLastRefactoring());
			return fPreviewPage;
		}
		return super.getNextPage(page);
	}

	/**
	 * Returns the preview wizard page.
	 * <p>
	 * Note: This API must not be called from outside the refactoring framework.
	 * </p>
	 *
	 * @return the preview wizard page
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final IPreviewWizardPage getPreviewPage() {
		return fPreviewPage;
	}

	/**
	 * {@inheritDoc}
	 */
	public IWizardPage getPreviousPage(final IWizardPage page) {
		if (page == fErrorPage || page == fPreviewPage)
			return null;
		return super.getPreviousPage(page);
	}

	/**
	 * Returns the refactoring descriptor of the current refactoring.
	 *
	 * @return the refactoring descriptor, or <code>null</code>
	 */
	private RefactoringDescriptorProxy getRefactoringDescriptor() {
		final RefactoringDescriptorProxy[] proxies= getRefactoringDescriptors();
		if (fCurrentRefactoring >= 0 && fCurrentRefactoring < proxies.length)
			return proxies[fCurrentRefactoring];
		return null;
	}

	/**
	 * Returns the refactoring descriptors in their order of execution.
	 *
	 * @return the refactoring descriptors
	 */
	private RefactoringDescriptorProxy[] getRefactoringDescriptors() {
		if (fDescriptorProxies == null) {
			final RefactoringDescriptorProxy[] proxies= fRefactoringHistory.getDescriptors();
			final RefactoringDescriptorProxy[] result= new RefactoringDescriptorProxy[proxies.length];
			System.arraycopy(proxies, 0, result, 0, proxies.length);
			RefactoringHistoryManager.sortRefactoringDescriptorsAscending(result);
			fDescriptorProxies= result;
		}
		return fDescriptorProxies;
	}

	/**
	 * Returns the first page of a refactoring.
	 *
	 * @return the first page, or <code>null</code>
	 */
	private IWizardPage getRefactoringPage() {
		final IWizardPage[] result= { null};
		final RefactoringStatus status= new RefactoringStatus();
		final IWizardContainer wizard= getContainer();
		final IRunnableWithProgress runnable= new IRunnableWithProgress() {

			public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				Assert.isNotNull(monitor);
				try {
					monitor.beginTask(RefactoringUIMessages.RefactoringHistoryWizard_preparing_refactoring, 220);
					result[0]= null;
					if (!fAboutToPerformFired) {
						try {
							status.merge(fireAboutToPerformHistory(new SubProgressMonitor(monitor, 50, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)));
						} finally {
							fAboutToPerformFired= true;
						}
					}
					final boolean last= isLastRefactoring();
					final RefactoringDescriptorProxy proxy= getRefactoringDescriptor();
					preparePreviewPage(status, proxy, last);
					prepareErrorPage(status, proxy, status.hasFatalError(), last || status.hasFatalError());
					fErrorPage.setRefactoring(null);
					if (!status.isOK()) {
						result[0]= fErrorPage;
					} else if (proxy != null) {
						final IRefactoringHistoryService service= RefactoringCore.getHistoryService();
						try {
							service.connect();
							final RefactoringDescriptor descriptor= proxy.requestDescriptor(new SubProgressMonitor(monitor, 10, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
							if (descriptor != null) {
								final RefactoringContext context= createRefactoringContext(descriptor, status, new SubProgressMonitor(monitor, 60, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
								try {
									if (context != null && status.isOK()) {
										final Refactoring refactoring= context.getRefactoring();
										fPreviewPage.setRefactoring(refactoring);
										fErrorPage.setRefactoring(refactoring);
										status.merge(checkConditions(refactoring, new SubProgressMonitor(monitor, 20, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), CheckConditionsOperation.INITIAL_CONDITONS));
										if (!status.isOK()) {
											prepareErrorPage(status, proxy, status.hasFatalError(), last);
											result[0]= fErrorPage;
										} else {
											status.merge(checkConditions(refactoring, new SubProgressMonitor(monitor, 65, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), CheckConditionsOperation.FINAL_CONDITIONS));
											if (!status.isOK()) {
												prepareErrorPage(status, proxy, status.hasFatalError(), last);
												result[0]= fErrorPage;
											} else {
												final Change change= createChange(refactoring, new SubProgressMonitor(monitor, 5, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
												getShell().getDisplay().syncExec(new Runnable() {
	
													public final void run() {
														fPreviewPage.setChange(change);
													}
												});
												result[0]= fPreviewPage;
											}
										}
									} else {
										prepareErrorPage(status, proxy, status.hasFatalError(), last);
										result[0]= fErrorPage;
									}
								} finally {
									if (context != null)
										context.dispose();
								}
							} else {
								status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringUIMessages.RefactoringHistoryWizard_error_resolving_refactoring));
								prepareErrorPage(status, proxy, status.hasFatalError(), last);
								result[0]= fErrorPage;
							}
						} finally {
							service.disconnect();
						}
					} else {
						prepareErrorPage(status, proxy, status.hasFatalError(), last);
						result[0]= fErrorPage;
					}
				} catch (CoreException exception) {
					throw new InvocationTargetException(exception);
				} catch (OperationCanceledException exception) {
					throw new InterruptedException(exception.getLocalizedMessage());
				} finally {
					monitor.done();
				}
			}
		};
		try {
			wizard.run(true, false, runnable);
		} catch (InvocationTargetException exception) {
			RefactoringUIPlugin.log(exception);
			final Throwable throwable= exception.getTargetException();
			if (throwable != null) {
				fErrorPage.setNextPageDisabled(isLastRefactoring());
				fErrorPage.setStatus(RefactoringStatus.createFatalErrorStatus(RefactoringUIMessages.RefactoringWizard_unexpected_exception_1));
				result[0]= fErrorPage;
			}
		} catch (InterruptedException exception) {
			// Stay on same page
			result[0]= null;
		}
		getContainer().updateButtons();
		return result[0];
	}

	/**
	 * Hook method which is called when all refactorings of the history have
	 * been executed. This method may be called from non-UI threads.
	 * <p>
	 * This method is guaranteed to be called exactly once during the lifetime
	 * of a refactoring history wizard. It is not guaranteed that the user
	 * interface has not already been disposed of. The default implementation
	 * does nothing and returns a refactoring status of severity
	 * {@link RefactoringStatus#OK}.
	 * </p>
	 * <p>
	 * Subclasses may reimplement this method to perform any special processing.
	 * </p>
	 *
	 * @param monitor
	 *            the progress monitor to use
	 *
	 * @return a status describing the outcome of the operation
	 */
	protected RefactoringStatus historyPerformed(final IProgressMonitor monitor) {
		Assert.isNotNull(monitor);
		return new RefactoringStatus();
	}

	/**
	 * Is the current refactoring the last one?
	 *
	 * @return <code>true</code> if it is the last one, <code>false</code>
	 *         otherwise
	 */
	private boolean isLastRefactoring() {
		return fCurrentRefactoring >= getRefactoringDescriptors().length - 1;
	}

	/**
	 * Is the current refactoring the second last one?
	 *
	 * @return <code>true</code> if it is the second last one,
	 *         <code>false</code> otherwise
	 */
	private boolean isSecondLastRefactoring() {
		return fCurrentRefactoring >= getRefactoringDescriptors().length - 2;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean performCancel() {
		if (fExecutedRefactorings > 0 && !fCancelException) {
			final IPreferenceStore store= RefactoringUIPlugin.getDefault().getPreferenceStore();
			if (!store.getBoolean(PREFERENCE_DO_NOT_WARN_UNDO_ON_CANCEL)) {
				final MessageFormat format= new MessageFormat(RefactoringUIMessages.RefactoringHistoryWizard_undo_message_pattern);
				final String message= RefactoringUIMessages.RefactoringHistoryWizard_undo_message_explanation;
				final String[] messages= { RefactoringUIMessages.RefactoringHistoryWizard_one_refactoring_undone + message, RefactoringUIMessages.RefactoringHistoryWizard_several_refactorings_undone + message};
				final ChoiceFormat choice= new ChoiceFormat(new double[] { 1, Double.MAX_VALUE}, messages);
				format.setFormatByArgumentIndex(0, choice);
				final MessageDialogWithToggle dialog= new MessageDialogWithToggle(getShell(), getShell().getText(), null, format.format(new Object[] { new Integer(fExecutedRefactorings)}), MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0, RefactoringUIMessages.RefactoringHistoryWizard_do_not_show_message, false);
				dialog.open();
				store.setValue(PREFERENCE_DO_NOT_WARN_UNDO_ON_CANCEL, dialog.getToggleState());
				if (dialog.getReturnCode() == 1)
					return false;
			}
			final IRunnableWithProgress runnable= new IRunnableWithProgress() {

				public final void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
					for (int index= 0; index < fExecutedRefactorings; index++) {
						try {
							RefactoringCore.getUndoManager().performUndo(null, new SubProgressMonitor(monitor, 100));
							if (fExecutedRefactorings > 0)
								fExecutedRefactorings--;
						} catch (CoreException exception) {
							throw new InvocationTargetException(exception);
						}
					}
				}
			};
			try {
				getContainer().run(false, false, runnable);
			} catch (InvocationTargetException exception) {
				RefactoringUIPlugin.log(exception);
				fCancelException= true;
				fErrorPage.setStatus(RefactoringStatus.createFatalErrorStatus(RefactoringUIMessages.RefactoringHistoryWizard_internal_error));
				fErrorPage.setNextPageDisabled(true);
				fErrorPage.setTitle(RefactoringUIMessages.RefactoringHistoryWizard_internal_error_title);
				fErrorPage.setDescription(RefactoringUIMessages.RefactoringHistoryWizard_internal_error_description);
				getContainer().showPage(fErrorPage);
				return false;
			} catch (InterruptedException exception) {
				// Does not happen
			}
		}
		return super.performCancel();
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean performFinish() {
		if (fHeadlessErrorStatus)
			return true;
		if (fOverviewPage != null)
			fOverviewPage.performFinish();
		final IWizardContainer wizard= getContainer();
		final RefactoringStatus status= new RefactoringStatus();
		final RefactoringDescriptorProxy[] proxies= getRefactoringDescriptors();
		final List list= new ArrayList(proxies.length);
		for (int index= fCurrentRefactoring; index < proxies.length; index++)
			list.add(proxies[index]);
		final RefactoringDescriptorProxy[] descriptors= new RefactoringDescriptorProxy[list.size()];
		list.toArray(descriptors);
		final boolean last= isLastRefactoring();
		if (wizard.getCurrentPage() == fPreviewPage && last) {
			final Refactoring refactoring= fPreviewPage.getRefactoring();
			final Change change= fPreviewPage.getChange();
			if (refactoring != null && change != null) {
				status.merge(performPreviewChange(change, refactoring));
				if (!status.isOK()) {
					final RefactoringStatusEntry entry= status.getEntryWithHighestSeverity();
					if (entry.getSeverity() == RefactoringStatus.INFO && entry.getCode() == RefactoringHistoryWizard.STATUS_CODE_INTERRUPTED)
						return false;
					fErrorPage.setStatus(status);
					fErrorPage.setNextPageDisabled(true);
					fErrorPage.setTitle(RefactoringUIMessages.RefactoringHistoryPreviewPage_apply_error_title);
					fErrorPage.setDescription(RefactoringUIMessages.RefactoringHistoryPreviewPage_apply_error);
					wizard.showPage(fErrorPage);
					return false;
				}
			}
		} else {
			final IPreferenceStore store= RefactoringUIPlugin.getDefault().getPreferenceStore();
			if (!store.getBoolean(PREFERENCE_DO_NOT_WARN_FINISH) && proxies.length > 0) {
				final MessageDialogWithToggle dialog= new MessageDialogWithToggle(getShell(), wizard.getShell().getText(), null, Messages.format(RefactoringUIMessages.RefactoringHistoryWizard_warning_finish, LegacyActionTools.removeMnemonics(IDialogConstants.FINISH_LABEL)), MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0, RefactoringUIMessages.RefactoringHistoryWizard_do_not_show_message, false);
				dialog.open();
				store.setValue(PREFERENCE_DO_NOT_WARN_FINISH, dialog.getToggleState());
				if (dialog.getReturnCode() == IDialogConstants.CANCEL_ID)
					return false;
			}
			final PerformRefactoringHistoryOperation operation= new PerformRefactoringHistoryOperation(new RefactoringHistoryImplementation(descriptors)) {

				protected RefactoringContext createRefactoringContext(final RefactoringDescriptor descriptor, final RefactoringStatus state, IProgressMonitor monitor) throws CoreException {
					return RefactoringHistoryWizard.this.createRefactoringContext(descriptor, state, monitor);
				}

				protected void refactoringPerformed(final Refactoring refactoring, final IProgressMonitor monitor) {
					SafeRunner.run(new ISafeRunnable() {

						public void handleException(final Throwable exception) {
							RefactoringUIPlugin.log(exception);
						}

						public final void run() throws Exception {
							RefactoringHistoryWizard.this.refactoringPerformed(refactoring, monitor);
						}
					});
				}

				public void run(final IProgressMonitor monitor) throws CoreException {
					try {
						monitor.beginTask(RefactoringUIMessages.RefactoringHistoryWizard_preparing_refactorings, 100);
						if (!fAboutToPerformFired) {
							try {
								status.merge(fireAboutToPerformHistory(new SubProgressMonitor(monitor, 20, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)));
							} finally {
								fAboutToPerformFired= true;
							}
						}
						if (!status.isOK()) {
							final int severity= status.getSeverity();
							throw new CoreException(new Status(severity != RefactoringStatus.FATAL ? severity : IStatus.ERROR, RefactoringUIPlugin.getPluginId(), 0, null, null));
						}
						super.run(new SubProgressMonitor(monitor, 80, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
					} finally {
						monitor.done();
					}
				}
			};
			try {
				wizard.run(false, false, new WorkbenchRunnableAdapter(operation, ResourcesPlugin.getWorkspace().getRoot()));
			} catch (InvocationTargetException exception) {
				RefactoringUIPlugin.log(exception);
				final Throwable throwable= exception.getTargetException();
				if (throwable != null) {
					final String message= throwable.getLocalizedMessage();
					if (message != null && !"".equals(message)) //$NON-NLS-1$
						status.merge(RefactoringStatus.createFatalErrorStatus(message));
					fErrorPage.setStatus(status);
					fErrorPage.setNextPageDisabled(status.hasFatalError());
					fErrorPage.setTitle(RefactoringUIMessages.RefactoringHistoryPreviewPage_apply_error_title);
					fErrorPage.setDescription(RefactoringUIMessages.RefactoringHistoryPreviewPage_apply_error);
					wizard.showPage(fErrorPage);
					return false;
				}
			} catch (InterruptedException exception) {
				// Does not happen
			}
			final RefactoringStatus result= operation.getExecutionStatus();
			if (!result.isOK()) {
				fHeadlessErrorStatus= true;
				fErrorPage.setStatus(result);
				fErrorPage.setNextPageDisabled(true);
				fErrorPage.setTitle(RefactoringUIMessages.RefactoringHistoryPreviewPage_finish_error_title);
				fErrorPage.setDescription(RefactoringUIMessages.RefactoringHistoryPreviewPage_finish_error_description);
				wizard.showPage(fErrorPage);
				return false;
			}
		}
		return true;
	}

	/**
	 * Performs the change previously displayed in the preview.
	 * <p>
	 * Note: This API must not be called from outside the refactoring framework.
	 * </p>
	 *
	 * @param change
	 *            the change displayed in the preview
	 * @param refactoring
	 *            the associated refactoring
	 * @return the status of the operation, already handled by the user
	 */
	public final RefactoringStatus performPreviewChange(final Change change, final Refactoring refactoring) {
		Assert.isNotNull(change);
		Assert.isNotNull(refactoring);
		final UIPerformChangeOperation operation= new UIPerformChangeOperation(getShell().getDisplay(), change, getContainer()) {

			public void run(final IProgressMonitor monitor) throws CoreException {
				try {
					monitor.beginTask(RefactoringUIMessages.RefactoringHistoryWizard_preparing_changes, 12);
					super.run(new SubProgressMonitor(monitor, 10, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
					refactoringPerformed(refactoring, new SubProgressMonitor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
				} finally {
					monitor.done();
				}
			}
		};
		final RefactoringStatus status= performPreviewChange(operation, refactoring);
		if (status.isOK())
			status.merge(operation.getValidationStatus());
		return status;
	}

	/**
	 * Performs the change previously displayed in the preview using the
	 * specified change operation.
	 *
	 * @param operation
	 *            the change operation
	 * @param refactoring
	 *            the associated refactoring
	 * @return the status of the operation, already handled by the user
	 */
	private RefactoringStatus performPreviewChange(final PerformChangeOperation operation, final Refactoring refactoring) {
		Assert.isNotNull(operation);
		Assert.isNotNull(refactoring);
		operation.setUndoManager(RefactoringCore.getUndoManager(), refactoring.getName());
		final IWizardContainer wizard= getContainer();
		final Shell shell= wizard.getShell();
		try {
			wizard.run(false, false, new WorkbenchRunnableAdapter(operation, ResourcesPlugin.getWorkspace().getRoot()));
		} catch (InvocationTargetException exception) {
			final Throwable throwable= exception.getTargetException();
			if (operation.changeExecutionFailed()) {
				final Change change= operation.getChange();
				final ChangeExceptionHandler handler= new ChangeExceptionHandler(shell, refactoring);
				if (throwable instanceof RuntimeException)
					handler.handle(change, (RuntimeException) throwable);
				else if (throwable instanceof CoreException)
					handler.handle(change, (CoreException) throwable);
			}
			ExceptionHandler.handle(exception, shell, RefactoringUIMessages.RefactoringWizard_refactoring, RefactoringUIMessages.RefactoringWizard_unexpected_exception_1);
		} catch (InterruptedException exception) {
			return RefactoringStatus.create(new Status(IStatus.INFO, RefactoringUIPlugin.getPluginId(), STATUS_CODE_INTERRUPTED, exception.getLocalizedMessage(), exception));
		} finally {
			fPreviewPage.setNextPageDisabled(isSecondLastRefactoring());
			getContainer().updateButtons();
		}
		return new RefactoringStatus();
	}

	/**
	 * Prepares the error page to be displayed.
	 *
	 * @param status
	 *            the refactoring status
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param fatal
	 *            <code>true</code> if the error is fatal, <code>false</code>
	 *            otherwise
	 * @param disabled
	 *            <code>true</code> if the next page is disabled,
	 *            <code>false</code> otherwise
	 */
	private void prepareErrorPage(final RefactoringStatus status, final RefactoringDescriptorProxy descriptor, final boolean fatal, final boolean disabled) {
		getShell().getDisplay().syncExec(new Runnable() {

			public final void run() {
				fErrorPage.setTitle(descriptor, fCurrentRefactoring, fDescriptorProxies.length);
				fErrorPage.setNextPageDisabled(disabled && fatal);
				fErrorPage.setPageComplete(!fatal);
				fErrorPage.setStatus(null);
				fErrorPage.setStatus(status);
				getContainer().updateButtons();
			}
		});
	}

	/**
	 * Prepares the preview page to be displayed.
	 *
	 * @param status
	 *            the refactoring status
	 * @param descriptor
	 *            the refactoring descriptor
	 * @param disabled
	 *            <code>true</code> if the next page is disabled,
	 *            <code>false</code> otherwise
	 */
	private void preparePreviewPage(final RefactoringStatus status, final RefactoringDescriptorProxy descriptor, final boolean disabled) {
		getShell().getDisplay().syncExec(new Runnable() {

			public final void run() {
				fPreviewPage.setTitle(descriptor, fCurrentRefactoring, fDescriptorProxies.length);
				fPreviewPage.setNextPageDisabled(disabled);
				fPreviewPage.setPageComplete(!disabled);
				fPreviewPage.setStatus(status);
				getContainer().updateButtons();
			}
		});
	}

	/**
	 * Hook method which is called when the specified refactoring has been
	 * performed, e.g. its change object has been successfully applied to the
	 * workspace. The default implementation does nothing and returns a
	 * refactoring status of severity {@link RefactoringStatus#OK}. This method
	 * may be called from non-UI threads.
	 * <p>
	 * Subclasses may reimplement this method to perform any special processing.
	 * </p>
	 * <p>
	 * Returning a status of severity {@link RefactoringStatus#FATAL} will
	 * terminate the execution of the refactorings.
	 * </p>
	 *
	 * @param refactoring
	 *            the refactoring which has been performed
	 * @param monitor
	 *            the progress monitor to use
	 * @return a status describing the outcome of the operation
	 */
	protected RefactoringStatus refactoringPerformed(final Refactoring refactoring, final IProgressMonitor monitor) {
		Assert.isNotNull(refactoring);
		Assert.isNotNull(monitor);
		fExecutedRefactorings++;
		return new RefactoringStatus();
	}

	/**
	 * Hook method which is called for each change before it is displayed in a
	 * preview page. The default implementation returns <code>true</code>.
	 * <p>
	 * Subclasses may reimplement this method to perform any special filtering
	 * of preview changes.
	 * </p>
	 *
	 * @param change
	 *            the change to select
	 * @return <code>true</code> if the change passes the filter,
	 *         <code>false</code> otherwise
	 */
	protected boolean selectPreviewChange(final Change change) {
		return true;
	}

	/**
	 * Hook method which is called for each status entry before it is displayed
	 * in a wizard page. The default implementation returns <code>true</code>.
	 * <p>
	 * Subclasses may reimplement this method to perform any special filtering
	 * of status entries on error pages.
	 * </p>
	 *
	 * @param entry
	 *            the status entry to select
	 * @return <code>true</code> if the status entry passes the filter,
	 *         <code>false</code> otherwise
	 */
	protected boolean selectStatusEntry(final RefactoringStatusEntry entry) {
		return true;
	}

	/**
	 * Sets the refactoring history control configuration.
	 * <p>
	 * This method must be called before opening the wizard in a dialog.
	 * </p>
	 *
	 * @param configuration
	 *            the configuration to set
	 */
	public final void setConfiguration(final RefactoringHistoryControlConfiguration configuration) {
		Assert.isNotNull(configuration);
		fControlConfiguration= configuration;
	}

	/**
	 * Sets the refactoring history.
	 * <p>
	 * This method must be called before opening the wizard in a dialog.
	 * </p>
	 *
	 * @param history
	 *            the refactoring history
	 */
	public final void setInput(final RefactoringHistory history) {
		Assert.isNotNull(history);
		fRefactoringHistory= history;
	}
}
