blob: 31d2bd149dac42cc19eaf57f9f894571982de2a0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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.ltk.ui.refactoring.history;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
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.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CheckConditionsOperation;
import org.eclipse.ltk.core.refactoring.CreateChangeOperation;
import org.eclipse.ltk.core.refactoring.IInitializableRefactoringComponent;
import org.eclipse.ltk.core.refactoring.IRefactoringContributionManager;
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.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.core.refactoring.participants.RefactoringArguments;
import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryImplementation;
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.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;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.dialogs.IDialogConstants;
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.osgi.util.NLS;
/**
* 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 wizard consists of 0 .. n user defined pages, 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. The 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. </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>
* <p>
* Note: This API is considered experimental and may change in the near future.
* </p>
*
* @see org.eclipse.ltk.core.refactoring.Refactoring
* @see org.eclipse.ltk.core.refactoring.history.RefactoringHistory
*
* @since 3.2
*/
public class RefactoringHistoryWizard extends Wizard {
/** 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$;
/**
* The status code representing an interrupted operation.
* <p>
* This constant is NOT official API. It is used by the refactoring UI
* plug-in to recognize a status of interrupted operations.
* </p>
*/
public static final int STATUS_CODE_INTERRUPTED= 10003;
/** Has the about to perform history event already been fired? */
private boolean fAboutToPerformFired= 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, or <code>null</code> */
private RefactoringDescriptorProxy[] fDescriptorProxies= null;
/** The error wizard page */
private final RefactoringHistoryErrorPage fErrorPage;
/** Are we currently in method <code>addPages</code>? */
private boolean fInAddPages= false;
/** The description of the overview page */
private final String fOverviewDescription;
/** The overview wizard page */
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;
/** 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 caption
* the caption of the wizard window
* @param title
* the title of the overview page
* @param description
* the description of the overview page
*/
public RefactoringHistoryWizard(final String caption, final String title, final String description) {
Assert.isNotNull(caption);
Assert.isNotNull(title);
Assert.isNotNull(description);
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);
}
/**
* 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. 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);
return new RefactoringStatus();
}
/**
* Hook method which is called before the a refactoring of the history is
* executed. The refactoring itself is in an uninitialized state at the time
* of the method call. The default implementation initializes the
* refactoring based on the refactoring arguments stored in the descriptor.
* 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 refactorings.
* </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);
final RefactoringStatus status= new RefactoringStatus();
if (refactoring instanceof IInitializableRefactoringComponent) {
final IInitializableRefactoringComponent component= (IInitializableRefactoringComponent) refactoring;
final RefactoringArguments arguments= RefactoringCore.getRefactoringContributionManager().createArguments(descriptor);
if (arguments != null)
status.merge(component.initialize(arguments));
else
status.addFatalError(Messages.format(RefactoringUIMessages.PerformRefactoringHistoryOperation_init_error, new String[] { descriptor.getDescription()}));
} else
status.addFatalError(Messages.format(RefactoringUIMessages.PerformRefactoringHistoryOperation_init_error, new String[] { descriptor.getDescription()}));
return status;
}
/**
* {@inheritDoc}
*
* Clients must contribute their wizard pages by reimplementing
* {@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);
fOverviewPage= new RefactoringHistoryOverviewPage(fRefactoringHistory, fOverviewTitle, fOverviewDescription, fControlConfiguration);
addPage(fOverviewPage);
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) {
final RefactoringStatus status= fErrorPage.getStatus();
return status == null || !status.hasFatalError();
}
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();
}
/**
* {@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(new Status(IStatus.ERROR, entry.getPluginId(), entry.getCode(), entry.getMessage(), null));
}
}
});
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 refactoring descriptor of the current refactoring.
*
* @return the refactoring descriptor, or <code>null</code>
*/
private RefactoringDescriptorProxy getCurrentDescriptor() {
final RefactoringDescriptorProxy[] proxies= getRefactoringDescriptors();
if (fCurrentRefactoring >= 0 && fCurrentRefactoring < proxies.length)
return proxies[fCurrentRefactoring];
return null;
}
/**
* Returns the refactoring associated with the current refactoring
* descriptor, in initialized state.
*
* @param descriptor
* the refactoring descriptor
*
* @param status
* the refactoring status
* @param monitor
* the progress monitor to use
* @return the refactoring, or <code>null</code>
* @throws CoreException
* if an error occurs while creating the refactoring
*/
private Refactoring getCurrentRefactoring(final RefactoringDescriptor descriptor, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
final IRefactoringContributionManager manager= RefactoringCore.getRefactoringContributionManager();
final Refactoring refactoring= manager.createRefactoring(descriptor);
if (refactoring != null) {
status.merge(aboutToPerformRefactoring(refactoring, descriptor, monitor));
if (!status.hasFatalError())
return refactoring;
} else
status.addFatalError(NLS.bind(RefactoringUIMessages.RefactoringHistoryWizard_error_instantiate_refactoring, descriptor.getDescription()));
return null;
}
/**
* Returns the error wizard page.
*
* @return the error wizard page
*/
public final IErrorWizardPage getErrorPage() {
return fErrorPage;
}
/**
* Converts a button label to pure text.
*
* @param label
* the button label
* @return the resulting text
*/
protected String getLabelAsText(final String label) {
Assert.isNotNull(label);
StringBuffer buffer= new StringBuffer(label.length());
for (int index= 0; index < label.length(); index++) {
char character= label.charAt(index);
if (character != '&')
buffer.append(character);
}
return buffer.toString();
}
/**
* {@inheritDoc}
*/
public IWizardPage getNextPage(final IWizardPage page) {
if (page == fOverviewPage) {
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= NLS.bind(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= MessageDialogWithToggle.openWarning(getShell(), wizard.getShell().getText(), message, RefactoringUIMessages.RefactoringHistoryWizard_do_not_show_message, false, null, null);
store.setValue(key, dialog.getToggleState());
}
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= getCurrentDescriptor();
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.
*
* @return the preview wizard page
*/
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 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);
Arrays.sort(result, new Comparator() {
public final int compare(final Object first, final Object second) {
final RefactoringDescriptorProxy predecessor= (RefactoringDescriptorProxy) first;
final RefactoringDescriptorProxy successor= (RefactoringDescriptorProxy) second;
return (int) (predecessor.getTimeStamp() - successor.getTimeStamp());
}
});
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= getCurrentDescriptor();
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.getRefactoringHistoryService();
try {
service.connect();
final RefactoringDescriptor descriptor= proxy.requestDescriptor(new SubProgressMonitor(monitor, 10, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
if (descriptor != null && !descriptor.isUnknown()) {
final Refactoring refactoring= getCurrentRefactoring(descriptor, status, new SubProgressMonitor(monitor, 60, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
if (refactoring != null && status.isOK()) {
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;
}
} 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;
}
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.
* </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 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= MessageDialogWithToggle.openWarning(getShell(), wizard.getShell().getText(), Messages.format(RefactoringUIMessages.RefactoringHistoryWizard_warning_finish, getLabelAsText(IDialogConstants.FINISH_LABEL)), RefactoringUIMessages.RefactoringHistoryWizard_do_not_show_message, false, null, null);
store.setValue(PREFERENCE_DO_NOT_WARN_FINISH, dialog.getToggleState());
}
final PerformRefactoringHistoryOperation operation= new PerformRefactoringHistoryOperation(new RefactoringHistoryImplementation(descriptors)) {
protected RefactoringStatus aboutToPerformRefactoring(final Refactoring refactoring, final RefactoringDescriptor descriptor, final IProgressMonitor monitor) {
final RefactoringStatus[] result= { new RefactoringStatus()};
SafeRunner.run(new ISafeRunnable() {
public void handleException(final Throwable exception) {
RefactoringUIPlugin.log(exception);
}
public final void run() throws Exception {
result[0]= RefactoringHistoryWizard.this.aboutToPerformRefactoring(refactoring, descriptor, monitor);
}
});
return result[0];
}
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())
throw new CoreException(new Status(status.getSeverity(), 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) {
// Just close wizard
}
}
return true;
}
/**
* Performs the change previously displayed in the preview.
* <p>
* This method is NOT official API. It is used by the refactoring UI plug-in
* to perform changes displayed in the preview page to the workspace.
* </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);
}
});
}
/**
* 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);
}
});
}
/**
* Hook method which is called when the specified refactoring has been
* performed, e.g. its change object has been successfully applied to the
* workspace. 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);
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>
* Note: This API must not be used from outside the refactoring framework.
* </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>
* Note: This API must not be used from outside the refactoring framework.
* </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;
}
}