/******************************************************************************* * Copyright (c) 2000, 2014 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 (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.rename; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringCore; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.ui.refactoring.RefactoringExecutionHelper; import org.eclipse.cdt.internal.ui.refactoring.RefactoringStarter; /** * Central access point to execute rename refactorings. */ public class RenameSupport { /** Flag indication that no additional update is to be performed. */ public static final int NONE= 0; /** Flag indicating that references are to be updated as well. */ public static final int UPDATE_REFERENCES= 1 << 0; /** * Flag indicating that textual matches in comments and in string literals * are to be updated as well. */ public static final int UPDATE_TEXTUAL_MATCHES= 1 << 6; /** Flag indicating that the getter method is to be updated as well. */ public static final int UPDATE_GETTER_METHOD= 1 << 4; /** Flag indicating that the setter method is to be updated as well. */ public static final int UPDATE_SETTER_METHOD= 1 << 5; /** @see #openDialog(Shell, CRenameRefactoring, DialogMode) */ private enum DialogMode { ALL_PAGES, PREVIEW_ONLY, CONDITIONAL_PREVIEW } /** @see #openDialog(Shell, CRenameRefactoring, DialogMode) */ private enum DialogResult { OK, CANCELED, SKIPPED } // Same as org.eclipse.ltk.internal.ui.refactoring.IErrorWizardPage#PAGE_NAME private static final String ERROR_PAGE_NAME = "ErrorPage"; //$NON-NLS-1$ private CRenameRefactoring fRefactoring; private RefactoringStatus fPreCheckStatus; /** * Executes some light weight precondition checking. If the returned status * is an error then the refactoring can't be executed at all. However, * returning an OK status doesn't guarantee that the refactoring can be * executed. It may still fail while performing the exhaustive precondition * checking done inside the methods <code>openDialog</code> or * <code>perform</code>. * * The method is mainly used to determine enablement/disablement of actions. * * @return the result of the light weight precondition checking. * * @throws CoreException if an unexpected exception occurs while performing the checking. * * @see #openDialog(Shell) * @see #perform(Shell, IWorkbenchWindow) */ public IStatus preCheck() throws CoreException { ensureChecked(); if (fPreCheckStatus.hasFatalError()) return fPreCheckStatus.getEntryMatchingSeverity(RefactoringStatus.FATAL).toStatus(); else return Status.OK_STATUS; } /** * Opens the refactoring dialog for this rename support. * * @param shell a shell used as a parent for the refactoring dialog. * @throws CoreException if an unexpected exception occurs while opening the dialog. * * @see #openDialog(Shell, boolean) */ public boolean openDialog(Shell shell) throws CoreException { return openDialog(shell, false); } /** * Opens the refactoring dialog for this rename support. * * <p> * This method has to be called from within the UI thread. * </p> * * @param shell a shell used as a parent for the refactoring, preview, or error dialog * @param showPreviewOnly if <code>true</code>, the dialog skips all user input pages and * directly shows the preview or error page. Otherwise, shows all pages. * @return <code>true</code> if the refactoring has been executed successfully, * <code>false</code> if it has been canceled or if an error has happened during * initial conditions checking. * * @throws CoreException if an error occurred while executing the * operation. * * @see #openDialog(Shell) */ public boolean openDialog(Shell shell, boolean showPreviewOnly) throws CoreException { ensureChecked(); if (fPreCheckStatus.hasFatalError()) { showInformation(shell, fPreCheckStatus); return false; } DialogMode mode = showPreviewOnly ? DialogMode.PREVIEW_ONLY : DialogMode.ALL_PAGES; return openDialog(shell, fRefactoring, mode) == DialogResult.OK; } /** * Opens the refactoring dialog for a given rename refactoring. * * @param shell a shell used as a parent for the refactoring dialog. * @param refactoring the refactoring object. * * @see #openDialog(Shell, boolean) */ public static void openDialog(Shell shell, CRenameRefactoring refactoring) { openDialog(shell, refactoring, DialogMode.ALL_PAGES); } /** * Opens the refactoring dialog. * * <p> * This method has to be called from within the UI thread. * </p> * * @param shell A shell used as a parent for the refactoring, preview, or error dialog * @param refactoring The refactoring. * @param mode One of DialogMode values. ALL_PAGES opens wizard with all pages shown; * PREVIEW_ONLY opens the preview page only; CONDITIONAL_PREVIEW opens the wizard with * preview page only and only if a warning was generated during the final conditions check. * @return One of DialogResult values. OK is returned if the dialog was shown and * the refactoring change was applied; CANCELED is returned if the refactoring was * cancelled. SKIPPED is returned if the dialog was skipped in CONDITIONAL_PREVIEW mode and * the refactoring change has not been applied yet. */ static DialogResult openDialog(Shell shell, CRenameRefactoring refactoring, final DialogMode mode) { try { final boolean[] dialogSkipped = new boolean[1]; CRenameRefactoringWizard wizard; if (mode == DialogMode.ALL_PAGES) { wizard = new CRenameRefactoringWizard(refactoring); } else { wizard = new CRenameRefactoringWizard(refactoring) { @Override protected void addUserInputPages() { // Nothing to add } @Override public IWizardPage getStartingPage() { IWizardPage startingPage = super.getStartingPage(); if (mode == DialogMode.CONDITIONAL_PREVIEW && !startingPage.getName().equals(ERROR_PAGE_NAME)) { dialogSkipped[0] = true; return null; } return startingPage; } }; wizard.setForcePreviewReview(mode != DialogMode.ALL_PAGES); } RefactoringStarter starter = new RefactoringStarter(); CRenameProcessor processor = refactoring.getProcessor(); processor.lockIndex(); try { RefactoringStatus status = processor.checkInitialConditions(new NullProgressMonitor()); if (status.hasFatalError()) { showInformation(shell, status); return DialogResult.CANCELED; } if (starter.activate(wizard, shell, RenameMessages.CRefactory_title_rename, processor.getSaveMode())) { return DialogResult.OK; } if (dialogSkipped[0]) { // If the dialog was not shown, it is our responsibility to close it. wizard.getContainer().getShell().close(); return DialogResult.SKIPPED; } } finally { processor.unlockIndex(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (CoreException e) { CUIPlugin.log(e); } return DialogResult.CANCELED; } /** * Executes the rename refactoring without showing a dialog to gather * additional user input (for example the new name of the <tt>ICElement</tt>). * Only an error dialog is shown (if necessary) to present the result * of the refactoring's full precondition checking. * <p> * The method has to be called from within the UI thread. * </p> * * @param parent a shell used as a parent for the error dialog. * @param context a {@link IRunnableContext} to execute the operation. * * @throws InterruptedException if the operation has been canceled by the * user. * @throws InvocationTargetException if an error occurred while executing the * operation. * * @see #openDialog(Shell) * @see IRunnableContext#run(boolean, boolean, org.eclipse.jface.operation.IRunnableWithProgress) */ public boolean perform(Shell parent, IWorkbenchWindow context) throws InterruptedException, InvocationTargetException { try { ensureChecked(); if (fPreCheckStatus.hasFatalError()) { showInformation(parent, fPreCheckStatus); return false; } CRenameProcessor renameProcessor = fRefactoring.getProcessor(); renameProcessor.lockIndex(); try { fPreCheckStatus = renameProcessor.checkInitialConditions(new NullProgressMonitor()); if (fPreCheckStatus.hasFatalError()) { showInformation(parent, fPreCheckStatus); return false; } DialogResult result = openDialog(context.getShell(), fRefactoring, DialogMode.CONDITIONAL_PREVIEW); switch (result) { case OK: return true; case SKIPPED: RefactoringExecutionHelper helper= new RefactoringExecutionHelper(fRefactoring, RefactoringCore.getConditionCheckingFailedSeverity(), renameProcessor.getSaveMode(), parent, context); Change change = fRefactoring.getChange(); Assert.isNotNull(change); helper.performChange(change, true); return true; default: return false; } } finally { renameProcessor.unlockIndex(); } } catch (CoreException e) { throw new InvocationTargetException(e); } } private RenameSupport(CRenameProcessor processor) { fRefactoring= new CRenameRefactoring(processor); } /** * Creates a new rename support for the given {@link ICProject}. * * @param processor the {@link CRenameProcessor} * @return the {@link RenameSupport}. * @throws CoreException if an unexpected error occurred while creating * the {@link RenameSupport}. */ public static RenameSupport create(CRenameProcessor processor) throws CoreException { return new RenameSupport(processor); } private void ensureChecked() throws CoreException { if (fPreCheckStatus == null) { if (!fRefactoring.isApplicable()) { fPreCheckStatus= RefactoringStatus.createFatalErrorStatus(RenameMessages.RenameSupport_not_available); } else { fPreCheckStatus= new RefactoringStatus(); } } } private static void showInformation(Shell parent, RefactoringStatus status) { String message= status.getMessageMatchingSeverity(RefactoringStatus.FATAL); MessageDialog.openInformation(parent, RenameMessages.RenameSupport_dialog_title, message); } }