/*******************************************************************************
 * Copyright (c) 2000, 2019 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.ltk.ui.refactoring;

import java.lang.reflect.InvocationTargetException;

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.resources.ResourcesPlugin;

import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.Wizard;

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.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringContext;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.internal.ui.refactoring.ChangeExceptionHandler;
import org.eclipse.ltk.internal.ui.refactoring.ErrorWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.ExceptionHandler;
import org.eclipse.ltk.internal.ui.refactoring.FinishResult;
import org.eclipse.ltk.internal.ui.refactoring.IErrorWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.IPreviewWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.InternalAPI;
import org.eclipse.ltk.internal.ui.refactoring.Messages;
import org.eclipse.ltk.internal.ui.refactoring.PreviewWizardPage;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringPluginImages;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIMessages;
import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIPlugin;
import org.eclipse.ltk.internal.ui.refactoring.WorkbenchRunnableAdapter;

/**
 * An abstract base implementation of a refactoring wizard. A refactoring
 * wizard differs from a normal wizard in the following characteristics:
 * <ul>
 *   <li>only pages of type {@link org.eclipse.ltk.ui.refactoring.RefactoringWizardPage
 *       RefactoringWizardPage} can be added to a refactoring wizard. Trying to
 *       add a different kind of page results in an exception.</li>
 *   <li>a refactoring wizard consists of 0 .. n user input pages, one error page
 *       to present the outcome of the refactoring's condition checking and one
 *       preview page to present a preview of the workspace changes.</li>
 * </ul>
 * <p>
 * A refactoring wizard is best opened using the {@link RefactoringWizardOpenOperation}.
 * </p>
 * <p>
 * Clients may extend this class.
 * </p>
 *
 * @see org.eclipse.ltk.core.refactoring.Refactoring
 *
 * @since 3.0
 */
public abstract class RefactoringWizard extends Wizard {

	/**
	 * Flag (value 0) indicating that no special flags are provided.
	 */
	public static final int NONE= 0;

	/**
	 * Flag (value 1) indicating that the initial condition checking of the refactoring is done when
	 * the wizard opens. If not specified it is assumed that the initial condition checking
	 * has been done by the client before opening the wizard dialog.
	 */
	public static final int CHECK_INITIAL_CONDITIONS_ON_OPEN= 1 << 0;

	/**
	 * Flag (value 2) indicating that a normal wizard based user interface consisting
	 * of a back, next, finish and cancel button should be used to present
	 * this refactoring wizard. This flag can't be specified together with
	 * the flag {@link #DIALOG_BASED_USER_INTERFACE}.
	 */
	public static final int WIZARD_BASED_USER_INTERFACE= 1 << 1;

	/**
	 * Flag (value 4) indicating that a lightweight dialog based user interface should
	 * be used to present this refactoring wizard. This user interface consists
	 * of a preview, finish and cancel button and the initial size of dialog
	 * is based on the first user input page. This flag is only valid if only
	 * one user input page is present. Specifying this flag together with more
	 * than one input page will result in an exception when adding the user input
	 * pages. This flag can't be specified together with the flag
	 * {@link #WIZARD_BASED_USER_INTERFACE}.
	 *
	 * @since 3.1
	 */
	public static final int DIALOG_BASED_USER_INTERFACE= 1 << 2;

	/**
	 * @deprecated Use {@link #DIALOG_BASED_USER_INTERFACE} instead.
	 */
	@Deprecated
	public static final int DIALOG_BASED_UESR_INTERFACE= DIALOG_BASED_USER_INTERFACE;

	/**
	 * Flag (value 8) indicating that the finish and cancel button should be named
	 * yes and no. The flag is ignored if the flag {@link #WIZARD_BASED_USER_INTERFACE}
	 * is specified.
	 */
	public static final int YES_NO_BUTTON_STYLE= 1 << 3;

	/**
	 * Flag (value 16) indicating that the wizard should not show a preview page.
	 * The flag is ignored if the flag {@link #WIZARD_BASED_USER_INTERFACE}
	 * is specified.
	 * */
	public static final int NO_PREVIEW_PAGE= 1 << 4;

	/**
	 * Flag (value 32) indicating that the first change node presented in the
	 * preview page should be fully expanded.
	 */
	public static final int PREVIEW_EXPAND_FIRST_NODE= 1 << 5;

	/**
	 * Flag (value 64) indicating that the dialog representing the refactoring
	 * status to the user will not contain a back button.
	 * The flag is ignored if the flag {@link #WIZARD_BASED_USER_INTERFACE}
	 * is specified.
	 */
	public static final int NO_BACK_BUTTON_ON_STATUS_DIALOG= 1 << 6;

	/**
	 * Flag (value 128) indicating that a help control should be shown.
	 * The flag is ignored if the flag {@link #WIZARD_BASED_USER_INTERFACE}
	 * is specified (the '?' button is always shown there).
	 *
	 * @see org.eclipse.jface.dialogs.TrayDialog#setHelpAvailable(boolean)
	 *
	 * @since 3.6
	 */
	public static final int SHOW_HELP_CONTROL= 1 << 7;

	private static final int LAST= 1 << 8;

	private final int fFlags;
	private final RefactoringContext fRefactoringContext;
	private final Refactoring fRefactoring;
	private String fDefaultPageTitle;

	private Change fChange;
	private RefactoringStatus fInitialConditionCheckingStatus= new RefactoringStatus();
	private RefactoringStatus fConditionCheckingStatus;

	private int fUserInputPages;
	private boolean fInAddPages;

	private boolean fIsChangeCreationCancelable;
	private boolean fForcePreviewReview;
	private boolean fPreviewShown;

	private IRunnableContext fRunnableContext;

	/**
	 * Creates a new refactoring wizard for the given refactoring.
	 *
	 * @param refactoring the refactoring the wizard is presenting
	 * @param flags flags specifying the behavior of the wizard. If neither
	 *  <code>WIZARD_BASED_USER_INTERFACE</code> nor <code>DIALOG_BASED_UESR_INTERFACE</code>
	 *  is specified then <code>WIZARD_BASED_USER_INTERFACE</code> will be
	 *  taken as a default.
	 */
	public RefactoringWizard(Refactoring refactoring, int flags) {
		this(null, refactoring, flags);
		Assert.isNotNull(refactoring);
	}

	/**
	 * Creates a new refactoring wizard for the given refactoring context.
	 *
	 * @param refactoringContext the refactoringContext the wizard is presenting
	 * @param flags flags specifying the behavior of the wizard. If neither
	 *  <code>WIZARD_BASED_USER_INTERFACE</code> nor <code>DIALOG_BASED_UESR_INTERFACE</code>
	 *  is specified then <code>WIZARD_BASED_USER_INTERFACE</code> will be
	 *  taken as a default.
	 * @since 3.7
	 */
	public RefactoringWizard(RefactoringContext refactoringContext, int flags) {
		this(refactoringContext, null, flags);
		Assert.isNotNull(refactoringContext);
	}

	private RefactoringWizard(RefactoringContext refactoringContext, Refactoring refactoring, int flags) {
		Assert.isTrue(flags < LAST);
		if ((flags & DIALOG_BASED_USER_INTERFACE) == 0)
			flags |= WIZARD_BASED_USER_INTERFACE;
		Assert.isTrue((flags & DIALOG_BASED_USER_INTERFACE) != 0 || (flags & WIZARD_BASED_USER_INTERFACE) != 0);
		fRefactoringContext= refactoringContext;
		fRefactoring= refactoring;
		fFlags= flags;
		setNeedsProgressMonitor(true);
		setChangeCreationCancelable(true);
		setWindowTitle(RefactoringUIMessages.RefactoringWizard_title);
		setDefaultPageImageDescriptor(RefactoringPluginImages.DESC_WIZBAN_REFACTOR);
	}

	//---- Setter and Getters ------------------------------------------------------------

	/**
	 * Returns the refactoring this wizard is associated with.
	 *
	 * @return the wizard's refactoring
	 */
	public final Refactoring getRefactoring() {
		return fRefactoring;
	}

	/**
	 * Returns the refactoring context this wizard is associated with, or <code>null</code> if none.
	 *
	 * @return the wizard's refactoring context or <code>null</code>
	 * @since 3.7
	 */
	public final RefactoringContext getRefactoringContext() {
		return fRefactoringContext;
	}

	/**
	 * Returns the refactoring wizard flags that have been set for this wizard.
	 * Note that the set of valid flags may grow in the future.
	 *
	 * @return the wizard's flags
	 *
	 * @since 3.6
	 */
	public final int getWizardFlags(){
		return fFlags;
	}

	/**
	 * Sets the default page title to the given value. This value is used
	 * as a page title for wizard pages which don't provide their own
	 * page title. Setting this value has only an effect as long as the
	 * user interface hasn't been created yet.
	 *
	 * @param defaultPageTitle the default page title.
	 * @see Wizard#setDefaultPageImageDescriptor(org.eclipse.jface.resource.ImageDescriptor)
	 */
	public final void setDefaultPageTitle(String defaultPageTitle) {
		Assert.isNotNull(defaultPageTitle);
		fDefaultPageTitle= defaultPageTitle;
	}

	/**
	 * Returns the default page title used for pages that don't provide their
	 * own page title.
	 *
	 * @return the default page title or <code>null</code> if non has been set
	 *
	 * @see #setDefaultPageTitle(String)
	 */
	public final String getDefaultPageTitle() {
		return fDefaultPageTitle;
	}

	/**
	 * If set to <code>true</code> the Finish or OK button, respectively will
	 * be disabled until the user has visited the preview page. If set to
	 * <code>false</code> the refactoring can be performed before the preview
	 * page has been visited.
	 *
	 * @param forcePreviewReview if <code>true</code> to user must confirm the
	 *  preview
	 */
	public final void setForcePreviewReview(boolean forcePreviewReview) {
		fForcePreviewReview= forcePreviewReview;
		IWizardContainer container= getContainer();
		if (container != null)
			container.updateButtons();
	}

	/**
	 * Returns the width in characters to be used for the message line embedded into
	 * the refactoring wizard dialog.
	 * <p>
	 * Subclasses may override this method and return a different value.
	 * </p>
	 *
	 * @return the message lines width in characters
	 */
	public int getMessageLineWidthInChars() {
		return 80;
	}

	/**
	 * If set to <code>true</code> the change creation is cancelable by the user.
	 * <p>
	 * By default, change creation is cancelable.
	 * </p>
	 * @param isChangeCreationCancelable determines whether the change creation
	 *  is cancelable by the user or not.
	 *
	 * @see Refactoring#createChange(IProgressMonitor)
	 */
	public final void setChangeCreationCancelable(boolean isChangeCreationCancelable){
		fIsChangeCreationCancelable= isChangeCreationCancelable;
	}

	/**
	 * Sets the initial condition checking status computed by the refactoring.
	 * Clients should uses this method if the initial condition checking
	 * status has been computed outside of this refactoring wizard.
	 *
	 * @param status the initial condition checking status.
	 *
	 * @see Refactoring#checkInitialConditions(IProgressMonitor)
	 * @see #CHECK_INITIAL_CONDITIONS_ON_OPEN
	 */
	public final void setInitialConditionCheckingStatus(RefactoringStatus status) {
		Assert.isNotNull(status);
		fInitialConditionCheckingStatus= status;
		setConditionCheckingStatus(status);
	}

	/**
	 * Returns the refactoring's change object or <code>null</code> if no change
	 * object has been created yet.
	 *
	 * @return the refactoring's change object or <code>null</code>
	 *
	 * @see Refactoring#createChange(IProgressMonitor)
	 */
	public final Change getChange() {
		return fChange;
	}

    /**
	 * @param b {@inheritDoc}
     * @deprecated {@link #WIZARD_BASED_USER_INTERFACE} always shows a '?' button.
	 *  To show the button with {@link #DIALOG_BASED_USER_INTERFACE}, add the {@link #SHOW_HELP_CONTROL} flag.
	 */
	@Deprecated
	@Override
	public void setHelpAvailable(boolean b) {
		super.setHelpAvailable(b);
	}

	/**
	 * Returns the status of the initial condition checking or <code>null</code>
	 * if the initial condition checking hasn't been performed yet.
	 *
	 * @return the status of the initial condition checking or <code>null</code>
	 *
	 * @see Refactoring#checkInitialConditions(IProgressMonitor)
	 */
	/* package */ final RefactoringStatus getInitialConditionCheckingStatus() {
		return fInitialConditionCheckingStatus;
	}

	/**
	 * Returns <code>true</code> if the wizard needs a wizard based user interface.
	 * Otherwise <code>false</code> is returned.
	 *
	 * @return whether the wizard needs a wizard based user interface or not
	 */
	/* package */ boolean needsWizardBasedUserInterface() {
		return (fFlags & WIZARD_BASED_USER_INTERFACE) != 0;
	}

	//---- Page management ------------------------------------------------------------

	/**
	 * {@inheritDoc}
	 *
	 * This method calls the hook method {@link #addUserInputPages()} to allow
	 * subclasses to add specific user input pages.
	 */
	@Override
	public final void addPages() {
		Assert.isNotNull(getRefactoring());
		try {
			fInAddPages= true;
			if (checkActivationOnOpen()) {
				internalCheckCondition(CheckConditionsOperation.INITIAL_CONDITONS);
			}
			if (fInitialConditionCheckingStatus.hasFatalError()) {
				addErrorPage();
				// Set the status since we added the error page
				setConditionCheckingStatus(getConditionCheckingStatus());
			} else {
				Assert.isTrue(getPageCount() == 0);
				addUserInputPages();
				fUserInputPages= getPageCount();
				if (fUserInputPages > 0) {
					IWizardPage[] pages= getPages();
					((UserInputWizardPage)pages[fUserInputPages - 1]).markAsLastUserInputPage();
				}
				addErrorPage();
				addPreviewPage();
			}
			initializeDefaultPageTitles();
		} finally {
			fInAddPages= false;
		}
	}

	/**
	 * {@inheritDoc}
	 *
	 * This method asserts that the pages added to the refactoring wizard
	 * are instances of type {@link RefactoringWizardPage}.
	 */
	@Override
	public final void addPage(IWizardPage page) {
		Assert.isTrue(page instanceof RefactoringWizardPage && fInAddPages);
		super.addPage(page);
	}

	/**
	 * Hook method to add user input pages to this refactoring wizard. Pages
	 * added via this call have to be instances of the type {@link UserInputWizardPage}.
	 * Adding pages of a different kind is not permitted and will result
	 * in unexpected behavior.
	 */
	protected abstract void addUserInputPages();

	private void addErrorPage(){
		addPage(new ErrorWizardPage());
	}

	private void addPreviewPage(){
		addPage(new PreviewWizardPage());
	}

	private boolean hasUserInput() {
		return fUserInputPages > 0;
	}

	private void initializeDefaultPageTitles() {
		if (fDefaultPageTitle == null)
			return;

		for (IWizardPage page : getPages()) {
			if (page.getTitle() == null)
				page.setTitle(fDefaultPageTitle);
		}
	}

	//---- Page computation -----------------------------------------------------------

	@Override
	public IWizardPage getStartingPage() {
		if (hasUserInput())
			return super.getStartingPage();

		IRunnableContext context= fRunnableContext != null ? fRunnableContext : PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		/*
		 * XXX: Can return null if there's no user input page and change creation has been cancelled.
		 * The only way to avoid this would be setChangeCreationCancelable(false).
		 */
		return computeUserInputSuccessorPage(null, context);
	}

	/**
	 * Sets the runnable context that will be used to computing refactoring conditions and change
	 * while the refactoring dialog is not yet shown. The default is to use the active workbench
	 * window.
	 *
	 * @param context a runnable context, or <code>null</code> to re-set the default
	 *
	 * @since 3.5
	 */
	public void setInitialComputationContext(IRunnableContext context) {
		fRunnableContext= context;
	}

	@Override
	public IWizardPage getPreviousPage(IWizardPage page) {
		if (hasUserInput())
			return super.getPreviousPage(page);
		if (! page.getName().equals(IErrorWizardPage.PAGE_NAME)){
			if (fConditionCheckingStatus.isOK())
				return null;
		}
		return super.getPreviousPage(page);
	}

	/* package */ IWizardPage computeUserInputSuccessorPage(IWizardPage caller, IRunnableContext context) {
		Change change= createChange(new CreateChangeOperation(
			new CheckConditionsOperation(getRefactoring(), CheckConditionsOperation.FINAL_CONDITIONS),
			RefactoringStatus.FATAL), true, context);
		// Status has been updated since we have passed true
		RefactoringStatus status= getConditionCheckingStatus();

		// Creating the change has been canceled
		if (change == null && status == null) {
			internalSetChange(InternalAPI.INSTANCE, change);
			return caller;
		}

		// Set change if we don't have fatal errors.
		if (!status.hasFatalError())
			internalSetChange(InternalAPI.INSTANCE, change);

		if (status.isOK()) {
			return getPage(IPreviewWizardPage.PAGE_NAME);
		} else {
			return getPage(IErrorWizardPage.PAGE_NAME);
		}
	}

	@Override
	public boolean canFinish() {
		if (fForcePreviewReview && !fPreviewShown)
			return false;
		return super.canFinish();
	}

	//---- Condition checking ------------------------------------------------------------

	/* package */ final RefactoringStatus checkFinalConditions() {
		return internalCheckCondition(CheckConditionsOperation.FINAL_CONDITIONS);
	}

	private RefactoringStatus internalCheckCondition(int style) {

		CheckConditionsOperation op= new CheckConditionsOperation(getRefactoring(), style);

		Exception exception= null;
		try {
			PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
				new WorkbenchRunnableAdapter(op, ResourcesPlugin.getWorkspace().getRoot()));
		} catch (InterruptedException | InvocationTargetException e) {
			exception= e;
		}
		RefactoringStatus status= null;
		if (exception != null) {
			RefactoringUIPlugin.log(exception);
			status= new RefactoringStatus();
			status.addFatalError(RefactoringUIMessages.RefactoringWizard_internal_error_1);
		} else {
			status= op.getStatus();
		}
		setConditionCheckingStatus(status, style);
		return status;
	}

	private void setConditionCheckingStatus(RefactoringStatus status, int style) {
		if ((style & CheckConditionsOperation.ALL_CONDITIONS) == CheckConditionsOperation.ALL_CONDITIONS)
			setConditionCheckingStatus(status);
		else if ((style & CheckConditionsOperation.INITIAL_CONDITONS) == CheckConditionsOperation.INITIAL_CONDITONS)
			setInitialConditionCheckingStatus(status);
		else if ((style & CheckConditionsOperation.FINAL_CONDITIONS) == CheckConditionsOperation.FINAL_CONDITIONS)
			setFinalConditionCheckingStatus(status);
	}

	private RefactoringStatus getConditionCheckingStatus() {
		return fConditionCheckingStatus;
	}

	/**
	 * Sets the refactoring status.
	 *
	 * @param status the refactoring status to set.
	 */
	/* package */ final void setConditionCheckingStatus(RefactoringStatus status) {
		ErrorWizardPage page= (ErrorWizardPage)getPage(IErrorWizardPage.PAGE_NAME);
		if (page != null)
			page.setStatus(status);
		fConditionCheckingStatus= status;
	}

	/**
	 * Sets the refactoring status returned from final condition checking. Any previously
	 * computed initial status is merged into the given status before it is set to the
	 * error page.
	 *
	 * @param status the final condition checking status to set
	 */
	private void setFinalConditionCheckingStatus(RefactoringStatus status) {
		RefactoringStatus newStatus= new RefactoringStatus();
		if (fInitialConditionCheckingStatus != null)
			newStatus.merge(fInitialConditionCheckingStatus);
		newStatus.merge(status);
		setConditionCheckingStatus(newStatus);
	}

	//---- Change management -------------------------------------------------------------

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 * @param operation the create change operation
	 * @param updateStatus flag indicating if status updating is requested
	 *
	 * @return the created change
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final Change internalCreateChange(InternalAPI api, CreateChangeOperation operation, boolean updateStatus) {
		Assert.isNotNull(api);
		IRunnableContext context= getContainer() != null ? getContainer() : fRunnableContext;
		return createChange(operation, updateStatus, context);
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 * @param op the perform change operation
	 *
	 * @return whether the finish ended OK or not
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final FinishResult internalPerformFinish(InternalAPI api, PerformChangeOperation op) {
		op.setUndoManager(RefactoringCore.getUndoManager(), getRefactoring().getName());
		Shell parent= getContainer().getShell();
		try{
			getContainer().run(true, true, new WorkbenchRunnableAdapter(op, ResourcesPlugin.getWorkspace().getRoot()));
		} catch (InvocationTargetException e) {
			Throwable inner= e.getTargetException();
			if (op.changeExecutionFailed()) {
				ChangeExceptionHandler handler= new ChangeExceptionHandler(parent, getRefactoring());
				if (inner instanceof RuntimeException) {
					handler.handle(op.getChange(), (RuntimeException)inner);
					return FinishResult.createException();
				} else if (inner instanceof CoreException) {
					handler.handle(op.getChange(), (CoreException)inner);
					return FinishResult.createException();
				}
			}
			ExceptionHandler.handle(e, parent,
				RefactoringUIMessages.RefactoringWizard_refactoring,
				RefactoringUIMessages.RefactoringWizard_unexpected_exception_1);
			return FinishResult.createException();
		} catch (InterruptedException e) {
			return FinishResult.createInterrupted();
		}
		return FinishResult.createOK();
	}

	private Change createChange(CreateChangeOperation operation, boolean updateStatus, IRunnableContext context){
		InvocationTargetException exception= null;
		try {
			context.run((context != PlatformUI.getWorkbench().getActiveWorkbenchWindow()), fIsChangeCreationCancelable, new WorkbenchRunnableAdapter(
				operation, ResourcesPlugin.getWorkspace().getRoot()));
		} catch (InterruptedException e) {
			setConditionCheckingStatus(null);
			return null;
		} catch (InvocationTargetException e) {
			exception= e;
		}

		if (updateStatus) {
			RefactoringStatus status= null;
			if (exception != null) {
				status= new RefactoringStatus();
				String msg= exception.getMessage();
				if (msg != null) {
					status.addFatalError(Messages.format(RefactoringUIMessages.RefactoringWizard_see_log, msg));
				} else {
					status.addFatalError(RefactoringUIMessages.RefactoringWizard_Internal_error);
				}
				RefactoringUIPlugin.log(exception);
			} else {
				status= operation.getConditionCheckingStatus();
			}
			setConditionCheckingStatus(status, operation.getConditionCheckingStyle());
		} else {
			if (exception != null)
				ExceptionHandler.handle(exception, getContainer().getShell(),
					RefactoringUIMessages.RefactoringWizard_refactoring,
					RefactoringUIMessages.RefactoringWizard_unexpected_exception);
		}
		Change change= operation.getChange();
		return change;
	}

	//---- Re-implementation of Wizard methods --------------------------------------------

	/**
	 * Calls {@link RefactoringWizardPage#performFinish()} on the currently active wizard page.
	 * Clients are not expected to extend this method to do lengthy processing
	 * (the {@link Refactoring} class should implement analysis and {@link Change} creation).
     *
     * @return <code>true</code> to indicate the finish request
     *   was accepted, and <code>false</code> to indicate
     *   that the finish request was refused
	 */
	@Override
	public boolean performFinish() {
		RefactoringWizardPage page= (RefactoringWizardPage)getContainer().getCurrentPage();
		return page.performFinish();
	}

	@Override
	public boolean performCancel() {
		if (fChange != null)
			fChange.dispose();
		return super.performCancel();
	}

	//---- Internal API, but public due to Java constraints ------------------------------

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 *
	 * @return whether the wizard has a preview page or not.
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final boolean internalHasPreviewPage(InternalAPI api) {
		Assert.isNotNull(api);
		return (fFlags & NO_PREVIEW_PAGE) == 0;
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 *
	 * @return whether yes no button style is requested
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final boolean internalIsYesNoStyle(InternalAPI api) {
		Assert.isNotNull(api);
		return (fFlags & YES_NO_BUTTON_STYLE) != 0;
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 *
	 * @return whether the first node of the preview is supposed to be expanded
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final boolean internalGetExpandFirstNode(InternalAPI api) {
		Assert.isNotNull(api);
		return (fFlags & PREVIEW_EXPAND_FIRST_NODE) != 0;
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 * @param change the change to set
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final void internalSetChange(InternalAPI api, Change change){
		Assert.isNotNull(api);
		IPreviewWizardPage page= (IPreviewWizardPage)getPage(IPreviewWizardPage.PAGE_NAME);
		if (page != null)
			page.setChange(change);
		fChange= change;
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 * @param shown a boolean indicating if the preview page has been shown or not
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final void internalSetPreviewShown(InternalAPI api, boolean shown) {
		Assert.isNotNull(api);
		fPreviewShown= shown;
		getContainer().updateButtons();
	}

	/**
	 * Note: This method is for internal use only. Clients are not allowed to call this method.
	 *
	 * @param api internal instance to avoid access from external clients
	 * @return whether to show a back button or not
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 */
	public final boolean internalShowBackButtonOnStatusDialog(InternalAPI api) {
		Assert.isNotNull(api);
		return (fFlags & NO_BACK_BUTTON_ON_STATUS_DIALOG) == 0;
	}

	//---- Helper methods to check style bits --------------------------------------------

	private boolean checkActivationOnOpen() {
		return (fFlags & CHECK_INITIAL_CONDITIONS_ON_OPEN) != 0;
	}
}
