blob: ea6f00b11933050be8cc8b688ccac05393e8b8f6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.refactoring;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.help.WorkbenchHelp;
import org.eclipse.ui.part.PageBook;
import org.eclipse.compare.CompareUI;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.refactoring.ComparePreviewer.CompareInput;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.util.ViewerPane;
import org.eclipse.jdt.internal.corext.refactoring.base.Change;
import org.eclipse.jdt.internal.corext.refactoring.base.ChangeContext;
import org.eclipse.jdt.internal.corext.refactoring.base.IChange;
import org.eclipse.jdt.internal.corext.refactoring.base.ICompositeChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChange.EditChange;
import org.eclipse.jdt.internal.corext.refactoring.nls.changes.CreateTextFileChange;
/**
* Presents the changes made by the refactoring.
* Consists of a tree of changes and a compare viewer that shows the differences.
*/
public class PreviewWizardPage extends RefactoringWizardPage implements IPreviewWizardPage {
// Dummy root node if input element isn't a composite change.
private static class DummyRootNode extends Change implements ICompositeChange {
private IChange[] fChildren;
public DummyRootNode(IChange change) {
fChildren= new IChange[] { change };
}
public IChange[] getChildren() {
return fChildren;
}
public String getName() {
return null;
}
public Object getModifiedLanguageElement() {
return null;
}
public IChange getUndoChange() {
return null;
}
public void perform(ChangeContext context, IProgressMonitor pm) {
}
}
private static class NullPreviewer implements IPreviewViewer {
private Label fLabel;
public NullPreviewer(Composite parent) {
fLabel= new Label(parent, SWT.CENTER | SWT.FLAT);
fLabel.setText(RefactoringMessages.getString("PreviewWizardPage.no_preview")); //$NON-NLS-1$
}
public void setInput(Object input) {
// do nothing
}
public void refresh() {
// do nothing
}
public Control getControl() {
return fLabel;
}
}
private class NextChange extends Action {
public NextChange() {
setImageDescriptor(CompareUI.DESC_ETOOL_NEXT);
setDisabledImageDescriptor(CompareUI.DESC_DTOOL_NEXT);
setHoverImageDescriptor(CompareUI.DESC_CTOOL_NEXT);
setToolTipText(RefactoringMessages.getString("PreviewWizardPage.next_Change")); //$NON-NLS-1$
WorkbenchHelp.setHelp(this, IJavaHelpContextIds.NEXT_CHANGE_ACTION);
}
public void run() {
fTreeViewer.revealNext();
}
}
private class PreviousChange extends Action {
public PreviousChange() {
setImageDescriptor(CompareUI.DESC_ETOOL_PREV);
setDisabledImageDescriptor(CompareUI.DESC_DTOOL_PREV);
setHoverImageDescriptor(CompareUI.DESC_CTOOL_PREV);
setToolTipText(RefactoringMessages.getString("PreviewWizardPage.previous_Change")); //$NON-NLS-1$
WorkbenchHelp.setHelp(this, IJavaHelpContextIds.PREVIOUS_CHANGE_ACTION);
}
public void run() {
fTreeViewer.revealPrevious();
}
}
private IChange fChange;
private ChangeElement fCurrentSelection;
private PageBook fPageContainer;
private Control fStandardPage;
private Control fNullPage;
private ChangeElementTreeViewer fTreeViewer;
private PageBook fPreviewContainer;
private IPreviewViewer fCurrentPreviewViewer;
private IPreviewViewer fNullPreviewer;
private ComparePreviewer fComparePreview;
/**
* Creates a new proposed changes wizard page.
*/
public PreviewWizardPage() {
super(PAGE_NAME);
setDescription(RefactoringMessages.getString("PreviewWizardPage.description")); //$NON-NLS-1$
}
/**
* Sets the given change. Setting the change initializes the tree viewer with
* the given change.
* @param change the new change.
*/
public void setChange(IChange change){
if (fChange == change)
return;
fChange= change;
setTreeViewerInput();
}
/**
* Creates the tree viewer to present the hierarchy of changes. Subclasses may override
* to create their own custom tree viewer.
*
* @return the tree viewer to present the hierarchy of changes
*/
protected ChangeElementTreeViewer createTreeViewer(Composite parent) {
return new ChangeElementTreeViewer(parent);
}
/**
* Creates the content provider used to fill the tree of changes. Subclasses may override
* to create their own custom tree content provider.
*
* @return the tree content provider used to fill the tree of changes
*/
protected ITreeContentProvider createTreeContentProvider() {
return new ChangeElementContentProvider();
}
/**
* Creates the label provider used to render the tree of changes. Subclasses may override
* to create their own custom label provider.
*
* @return the label provider used to render the tree of changes
*/
protected ILabelProvider createTreeLabelProvider() {
return new ChangeElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT | JavaElementLabelProvider.SHOW_SMALL_ICONS);
}
/**
* Returns the <code>CompareInput</code> element, if the preview for the given
* <code>ChangeElement</code> can be presented in a compare viewer. The method
* may return <code>null</code> indicating that the preview cannot be displayed using
* a compare viewer.
* <p>
* Subclasses may override to provide their own input element.
*
* @return the compare input if the preview for the given change element can be
* presented using a compare viewer; otherwise <code>null</code>.
*/
protected CompareInput getCompareInput(ChangeElement element) {
try {
if (element instanceof DefaultChangeElement) {
IChange change= ((DefaultChangeElement)element).getChange();
if (change instanceof TextChange) {
TextChange cuc= (TextChange)change;
String type= ComparePreviewer.TEXT_TYPE;
if (change instanceof CompilationUnitChange)
type= ComparePreviewer.JAVA_TYPE;
return createCompareInput(
element,
cuc.getCurrentContent(),
cuc.getPreviewContent(),
type);
} else if (change instanceof CreateTextFileChange){
CreateTextFileChange ctfc= (CreateTextFileChange)change;
String type= ctfc.isJavaFile() ? ComparePreviewer.JAVA_TYPE: ComparePreviewer.TEXT_TYPE;
return createCompareInput(
element,
ctfc.getCurrentContent(),
ctfc.getPreview(),
type);
}
} else if (element instanceof TextEditChangeElement) {
EditChange tec= ((TextEditChangeElement)element).getTextEditChange();
TextChange change= tec.getTextChange();
if (change instanceof CompilationUnitChange) {
ISourceReference sourceReference= findSourceReference(element);
if (sourceReference != null) {
CompilationUnitChange cuc= (CompilationUnitChange)change;
return createCompareInput(
element,
cuc.getCurrentContent(sourceReference),
cuc.getPreviewContent(sourceReference, new EditChange[] {tec}),
ComparePreviewer.JAVA_TYPE);
}
}
return createCompareInput(
element,
change.getCurrentContent(tec, 2),
change.getPreviewContent(tec, 2),
ComparePreviewer.TEXT_TYPE);
} else if (element instanceof PseudoJavaChangeElement) {
PseudoJavaChangeElement pjce= (PseudoJavaChangeElement)element;
List l= collectTextEditChanges(pjce);
if (l.size() > 0) {
EditChange[] changes= (EditChange[]) l.toArray(new EditChange[l.size()]);
CompilationUnitChange change= (CompilationUnitChange)changes[0].getTextChange();
ISourceReference sourceReference= (ISourceReference)pjce.getJavaElement();
return createCompareInput(
element,
change.getCurrentContent(sourceReference),
change.getPreviewContent(sourceReference, changes),
ComparePreviewer.JAVA_TYPE);
}
}
} catch (CoreException e) {
ExceptionHandler.handle(e, getShell(), RefactoringMessages.getString("PreviewWizardPage.showing_preview"), RefactoringMessages.getString("PreviewWizardPage.exception")); //$NON-NLS-1$ //$NON-NLS-2$
}
return null;
}
private CompareInput createCompareInput(ChangeElement element, String left, String right, String type) {
if (element == null || left == null || right == null || type == null)
return null;
return new CompareInput(element, left, right, type);
}
/**
* Returns a viewer used to show a preview for the given change element. The returned viewer
* is kept referenced until the wizard page gets disposed. So it is up to the implementor of this
* method to reuse existing viewers over different change elements. The method may return
* <code>nulll</code> indicating that no preview is available for the given change element.
* <p>
* Subclasses may override to provide their own preview.
*
* @param element the change element for which a preview control is requested
* @param currentViewer the currently used preview viewer
* @param parent the parent to be used if a new preview viewer must be created
* @return the viewer to show a preview for the given change element
*/
protected IPreviewViewer getPreviewer(ChangeElement element, IPreviewViewer currentViewer, Composite parent) {
CompareInput input= getCompareInput(element);
if (input != null) {
fComparePreview.setInput(input);
return fComparePreview;
}
return null;
}
/* (non-JavaDoc)
* Method defined in RefactoringWizardPage
*/
protected boolean performFinish() {
return getRefactoringWizard().performFinish(new PerformChangeOperation(fChange));
}
/* (non-JavaDoc)
* Method defined in IWizardPage
*/
public boolean canFlipToNextPage() {
return false;
}
/* (Non-JavaDoc)
* Method defined in IWizardPage
*/
public void createControl(Composite parent) {
initializeDialogUnits(parent);
fPageContainer= new PageBook(parent, SWT.NONE);
fStandardPage= createStandardPreviewPage(fPageContainer);
fNullPage= createNullPage(fPageContainer);
setControl(fPageContainer);
WorkbenchHelp.setHelp(getControl(), IJavaHelpContextIds.REFACTORING_PREVIEW_WIZARD_PAGE);
}
private Composite createStandardPreviewPage(Composite parent) {
// XXX The composite is needed to limit the width of the SashForm. See http://bugs.eclipse.org/bugs/show_bug.cgi?id=6854
Composite result= new Composite(parent, SWT.NONE);
GridLayout layout= new GridLayout();
layout.marginHeight= 0; layout.marginWidth= 0;
result.setLayout(layout);
SashForm sashForm= new SashForm(result, SWT.VERTICAL);
ViewerPane pane= new ViewerPane(sashForm, SWT.BORDER | SWT.FLAT);
pane.setText(RefactoringMessages.getString("PreviewWizardPage.changes")); //$NON-NLS-1$
ToolBarManager tbm= pane.getToolBarManager();
tbm.add(new NextChange());
tbm.add(new PreviousChange());
tbm.update(true);
fTreeViewer= createTreeViewer(pane);
fTreeViewer.setContentProvider(createTreeContentProvider());
fTreeViewer.setLabelProvider(createTreeLabelProvider());
fTreeViewer.addSelectionChangedListener(createSelectionChangedListener());
fTreeViewer.addCheckStateListener(createCheckStateListener());
pane.setContent(fTreeViewer.getControl());
setTreeViewerInput();
fPreviewContainer= new PageBook(sashForm, SWT.NONE);
fComparePreview= new ComparePreviewer(fPreviewContainer);
fNullPreviewer= new NullPreviewer(fPreviewContainer);
fPreviewContainer.showPage(fNullPreviewer.getControl());
sashForm.setWeights(new int[]{33, 67});
GridData gd= new GridData(GridData.FILL_BOTH);
gd.widthHint= convertWidthInCharsToPixels(80);
sashForm.setLayoutData(gd);
Dialog.applyDialogFont(result);
return result;
}
private Control createNullPage(Composite parent) {
Composite result= new Composite(parent, SWT.NONE);
GridLayout layout= new GridLayout();
layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
result.setLayout(layout);
Label label= new Label(result, SWT.CENTER);
label.setText(RefactoringMessages.getString("PreviewWizardPage.no_source_code_change")); //$NON-NLS-1$
label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Dialog.applyDialogFont(result);
return result;
}
/* (Non-JavaDoc)
* Method defined in IWizardPage
*/
public void setVisible(boolean visible) {
fCurrentSelection= null;
if (hasChanges()) {
fPageContainer.showPage(fStandardPage);
ChangeElement treeViewerInput= (ChangeElement)fTreeViewer.getInput();
if (visible && treeViewerInput != null) {
IStructuredSelection selection= (IStructuredSelection)fTreeViewer.getSelection();
if (selection.isEmpty()) {
ITreeContentProvider provider= (ITreeContentProvider)fTreeViewer.getContentProvider();
Object[] elements= provider.getElements(treeViewerInput);
if (elements != null && elements.length > 0) {
Object element= elements[0];
if (getRefactoringWizard().getExpandFirstNode()) {
Object[] subElements= provider.getElements(element);
if (subElements != null && subElements.length > 0) {
fTreeViewer.expandToLevel(element, 999);
}
}
fTreeViewer.setSelection(new StructuredSelection(element));
}
}
}
super.setVisible(visible);
fTreeViewer.getControl().setFocus();
} else {
fPageContainer.showPage(fNullPage);
super.setVisible(visible);
}
getRefactoringWizard().setPreviewShown(visible);
}
private void setTreeViewerInput() {
ChangeElement input;
if (fChange == null) {
input= null;
} else if (fChange instanceof ICompositeChange && !(fChange instanceof TextChange)) {
input= new DefaultChangeElement(null, fChange);
} else {
input= new DefaultChangeElement(null, new DummyRootNode(fChange));
}
if (fTreeViewer != null) {
fTreeViewer.setInput(input);
}
}
private ICheckStateListener createCheckStateListener() {
return new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event){
ChangeElement element= (ChangeElement)event.getElement();
if (isChild(fCurrentSelection, element) || isChild(element, fCurrentSelection)) {
showPreview(fCurrentSelection);
}
}
private boolean isChild(ChangeElement element, ChangeElement child) {
while (child != null) {
if (child == element)
return true;
child= child.getParent();
}
return false;
}
};
}
private ISelectionChangedListener createSelectionChangedListener() {
return new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection sel= (IStructuredSelection) event.getSelection();
if (sel.size() == 1) {
ChangeElement newSelection= (ChangeElement)sel.getFirstElement();
if (newSelection != fCurrentSelection) {
fCurrentSelection= newSelection;
showPreview(newSelection);
}
} else {
showPreview(null);
}
}
};
}
private void showPreview(ChangeElement element) {
if (element != null)
fCurrentPreviewViewer= getPreviewer(element, fCurrentPreviewViewer, fPreviewContainer);
else
fCurrentPreviewViewer= null;
if (fCurrentPreviewViewer == null)
fCurrentPreviewViewer= fNullPreviewer;
fPreviewContainer.showPage(fCurrentPreviewViewer.getControl());
}
private ISourceReference findSourceReference(ChangeElement element) {
if (element == null) {
return null;
} else if (element instanceof PseudoJavaChangeElement) {
return (ISourceReference)((PseudoJavaChangeElement)element).getJavaElement();
} else if (element instanceof DefaultChangeElement) {
IChange change= ((DefaultChangeElement)element).getChange();
if (change instanceof CompilationUnitChange) {
return ((CompilationUnitChange)change).getCompilationUnit();
}
}
return findSourceReference(element.getParent());
}
private List collectTextEditChanges(PseudoJavaChangeElement element) {
List result= new ArrayList(10);
ChangeElement[] children= element.getChildren();
for (int i= 0; i < children.length; i++) {
ChangeElement child= children[i];
if (child instanceof TextEditChangeElement) {
result.add(((TextEditChangeElement)child).getTextEditChange());
} else if (child instanceof PseudoJavaChangeElement) {
result.addAll(collectTextEditChanges((PseudoJavaChangeElement)child));
}
}
return result;
}
/**
* Returns <code>true</code> if the preview page will show any changes when
* it becomes visibile. Otherwise <code>false</code> is returned.
*/
public boolean hasChanges() {
if (fChange == null)
return false;
if (fChange instanceof ICompositeChange)
return ((ICompositeChange)fChange).getChildren().length > 0;
return true;
}
}