blob: c680ae420923367c240d7e0404017fd0c5e94e52 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 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.jdt.internal.ui.refactoring;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.window.Window;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.refactoring.structure.PushDownRefactoringProcessor;
import org.eclipse.jdt.internal.corext.refactoring.structure.PushDownRefactoringProcessor.MemberActionInfo;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.util.TableLayoutComposite;
public final class PushDownWizard extends RefactoringWizard {
private final PushDownRefactoringProcessor fProcessor;
private static class PushDownInputPage extends UserInputWizardPage {
private static class MemberActionInfoLabelProvider extends LabelProvider implements ITableLabelProvider {
private static String getActionLabel(final int action) {
switch (action) {
case MemberActionInfo.NO_ACTION:
return ""; //$NON-NLS-1$
case MemberActionInfo.PUSH_ABSTRACT_ACTION:
return RefactoringMessages.PushDownInputPage_leave_abstract;
case MemberActionInfo.PUSH_DOWN_ACTION:
return RefactoringMessages.PushDownInputPage_push_down;
default:
Assert.isTrue(false);
return null;
}
}
private static String[] getAvailableActionLabels(final MemberActionInfo info) {
final int[] actions= info.getAvailableActions();
final String[] result= new String[actions.length];
for (int index= 0; index < actions.length; index++) {
result[index]= getActionLabel(actions[index]);
}
return result;
}
private final ILabelProvider fLabelProvider= new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT | JavaElementLabelProvider.SHOW_SMALL_ICONS);
@Override
public void dispose() {
fLabelProvider.dispose();
super.dispose();
}
@Override
public Image getColumnImage(final Object element, final int index) {
final MemberActionInfo info= (MemberActionInfo) element;
switch (index) {
case MEMBER_COLUMN:
return fLabelProvider.getImage(info.getMember());
case ACTION_COLUMN:
return null;
default:
Assert.isTrue(false);
return null;
}
}
@Override
public String getColumnText(final Object element, final int index) {
final MemberActionInfo info= (MemberActionInfo) element;
switch (index) {
case MEMBER_COLUMN:
return fLabelProvider.getText(info.getMember());
case ACTION_COLUMN:
return getActionLabel(info.getAction());
default:
Assert.isTrue(false);
return null;
}
}
}
private class PushDownCellModifier implements ICellModifier {
@Override
public boolean canModify(final Object element, final String property) {
if (!ACTION_PROPERTY.equals(property))
return false;
return ((MemberActionInfo) element).isEditable();
}
@Override
public Object getValue(final Object element, final String property) {
if (!ACTION_PROPERTY.equals(property))
return null;
final MemberActionInfo info= (MemberActionInfo) element;
return Integer.valueOf(info.getAction());
}
@Override
public void modify(final Object element, final String property, final Object value) {
if (!ACTION_PROPERTY.equals(property))
return;
final int action= ((Integer) value).intValue();
MemberActionInfo info;
if (element instanceof Item) {
info= (MemberActionInfo) ((Item) element).getData();
} else
info= (MemberActionInfo) element;
if (!canModify(info, property))
return;
info.setAction(action);
PushDownInputPage.this.updateWizardPage(null, true);
}
}
private static final int ACTION_COLUMN= 1;
private final static String ACTION_PROPERTY= "action"; //$NON-NLS-1$
private static final int MEMBER_COLUMN= 0;
private final static String MEMBER_PROPERTY= "member"; //$NON-NLS-1$
private static final String PAGE_NAME= "PushDownInputPage"; //$NON-NLS-1$
private static final int ROW_COUNT= 10;
private static int countEditableInfos(final MemberActionInfo[] infos) {
int result= 0;
for (MemberActionInfo info : infos) {
if (info.isEditable()) {
result++;
}
}
return result;
}
private static void setInfoAction(final MemberActionInfo[] infos, final int action) {
for (MemberActionInfo info : infos) {
info.setAction(action);
}
}
private Button fDeselectAllButton;
private Button fEditButton;
private Button fSelectAllButton;
private Label fStatusLine;
private PullPushCheckboxTableViewer fTableViewer;
private final PushDownRefactoringProcessor fProcessor;
public PushDownInputPage(PushDownRefactoringProcessor processor) {
super(PAGE_NAME);
fProcessor= processor;
}
private boolean areAllElementsMarkedAsNoAction() {
return countInfosForAction(MemberActionInfo.NO_ACTION) == ((MemberActionInfo[]) fTableViewer.getInput()).length;
}
private boolean areAllElementsMarkedAsPushDownAction() {
return countInfosForAction(MemberActionInfo.PUSH_DOWN_ACTION) == ((MemberActionInfo[]) fTableViewer.getInput()).length;
}
private void checkPageCompletionStatus(final boolean displayErrorMessage) {
if (areAllElementsMarkedAsNoAction()) {
if (displayErrorMessage)
setErrorMessage(RefactoringMessages.PushDownInputPage_Select_members_to_push_down);
setPageComplete(false);
} else {
setErrorMessage(null);
setPageComplete(true);
}
}
private int countInfosForAction(final int action) {
int count= 0;
for (MemberActionInfo info : (MemberActionInfo[]) fTableViewer.getInput()) {
if (info.getAction() == action)
count++;
}
return count;
}
private void createButtonComposite(final Composite parent) {
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
final GridLayout layout= new GridLayout();
layout.marginHeight= 0;
layout.marginWidth= 0;
composite.setLayout(layout);
fSelectAllButton= new Button(composite, SWT.PUSH);
fSelectAllButton.setText(RefactoringMessages.PullUpWizard_select_all_label);
fSelectAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
fSelectAllButton.setEnabled(true);
SWTUtil.setButtonDimensionHint(fSelectAllButton);
fSelectAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent event) {
final IMember[] members= getMembers();
setActionForMembers(members, MemberActionInfo.PUSH_DOWN_ACTION);
updateWizardPage(null, true);
}
});
fDeselectAllButton= new Button(composite, SWT.PUSH);
fDeselectAllButton.setText(RefactoringMessages.PullUpWizard_deselect_all_label);
fDeselectAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
fDeselectAllButton.setEnabled(false);
SWTUtil.setButtonDimensionHint(fDeselectAllButton);
fDeselectAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent event) {
final IMember[] members= getMembers();
setActionForMembers(members, MemberActionInfo.NO_ACTION);
updateWizardPage(null, true);
}
});
fEditButton= new Button(composite, SWT.PUSH);
fEditButton.setText(RefactoringMessages.PushDownInputPage_Edit);
final GridData data= new GridData(GridData.FILL_HORIZONTAL);
data.verticalIndent= new PixelConverter(parent).convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
fEditButton.setLayoutData(data);
fEditButton.setEnabled(false);
SWTUtil.setButtonDimensionHint(fEditButton);
fEditButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent event) {
PushDownInputPage.this.editSelectedMembers();
}
});
final Button addButton= new Button(composite, SWT.PUSH);
addButton.setText(RefactoringMessages.PushDownInputPage_Add_Required);
addButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
SWTUtil.setButtonDimensionHint(addButton);
addButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent event) {
PushDownInputPage.this.markAdditionalRequiredMembersAsMembersToPushDown();
}
});
}
@Override
public void createControl(final Composite parent) {
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout());
createMemberTableLabel(composite);
createMemberTableComposite(composite);
createStatusLine(composite);
setControl(composite);
Dialog.applyDialogFont(composite);
PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IJavaHelpContextIds.PUSH_DOWN_WIZARD_PAGE);
}
private void createMemberTable(final Composite parent) {
final TableLayoutComposite layouter= new TableLayoutComposite(parent, SWT.NONE);
layouter.addColumnData(new ColumnWeightData(60, true));
layouter.addColumnData(new ColumnWeightData(40, true));
final Table table= new Table(layouter, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | SWT.CHECK);
table.setHeaderVisible(true);
table.setLinesVisible(true);
final GridData data= new GridData(GridData.FILL_BOTH);
data.heightHint= SWTUtil.getTableHeightHint(table, ROW_COUNT);
data.widthHint= convertWidthInCharsToPixels(30);
layouter.setLayoutData(data);
final TableLayout layout= new TableLayout();
table.setLayout(layout);
final TableColumn first= new TableColumn(table, SWT.NONE);
first.setText(RefactoringMessages.PushDownInputPage_Member);
final TableColumn second= new TableColumn(table, SWT.NONE);
second.setText(RefactoringMessages.PushDownInputPage_Action);
fTableViewer= new PullPushCheckboxTableViewer(table);
fTableViewer.setUseHashlookup(true);
fTableViewer.setContentProvider(ArrayContentProvider.getInstance());
fTableViewer.setLabelProvider(new MemberActionInfoLabelProvider());
fTableViewer.addSelectionChangedListener(event -> PushDownInputPage.this.updateButtonEnablementState((IStructuredSelection) event.getSelection()));
fTableViewer.addCheckStateListener(event -> {
final boolean checked= event.getChecked();
final MemberActionInfo info= (MemberActionInfo) event.getElement();
if (checked)
info.setAction(MemberActionInfo.PUSH_DOWN_ACTION);
else
info.setAction(MemberActionInfo.NO_ACTION);
updateWizardPage(null, true);
});
fTableViewer.addDoubleClickListener(event -> PushDownInputPage.this.editSelectedMembers());
fTableViewer.setInput(fProcessor.getMemberActionInfos());
updateWizardPage(null, false);
setupCellEditors(table);
}
private void createMemberTableComposite(final Composite parent) {
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
final GridLayout layout= new GridLayout();
layout.numColumns= 2;
layout.marginWidth= 0;
layout.marginHeight= 0;
composite.setLayout(layout);
createMemberTable(composite);
createButtonComposite(composite);
}
private void createMemberTableLabel(final Composite parent) {
final Label label= new Label(parent, SWT.NONE);
label.setText(RefactoringMessages.PushDownInputPage_Specify_actions);
label.setLayoutData(new GridData());
}
private void createStatusLine(final Composite composite) {
fStatusLine= new Label(composite, SWT.NONE);
final GridData data= new GridData(SWT.FILL, SWT.CENTER, false, false);
data.horizontalSpan= 2;
updateStatusLine();
fStatusLine.setLayoutData(data);
}
// String -> Integer
private Map<String, Integer> createStringMappingForSelectedElements() {
final Map<String, Integer> result= new HashMap<>();
int action= MemberActionInfo.PUSH_DOWN_ACTION;
result.put(MemberActionInfoLabelProvider.getActionLabel(action), Integer.valueOf(action));
int action1= MemberActionInfo.PUSH_ABSTRACT_ACTION;
result.put(MemberActionInfoLabelProvider.getActionLabel(action1), Integer.valueOf(action1));
return result;
}
private void editSelectedMembers() {
if (!fEditButton.isEnabled())
return;
final ISelection preserved= fTableViewer.getSelection();
try {
MemberActionInfo[] selectedMembers= getSelectedMemberActionInfos();
final String labelText= selectedMembers.length == 1 ? Messages.format(RefactoringMessages.PushDownInputPage_Mark_selected_members_singular, JavaElementLabels.getElementLabel(
selectedMembers[0].getMember(), JavaElementLabels.M_PARAMETER_TYPES)) : Messages.format(RefactoringMessages.PushDownInputPage_Mark_selected_members_plural, String
.valueOf(selectedMembers.length));
final Map<String, Integer> stringMapping= createStringMappingForSelectedElements();
final String[] keys= stringMapping.keySet().toArray(new String[stringMapping.size()]);
Arrays.sort(keys);
final int initialSelectionIndex= getInitialSelectionIndexForEditDialog(stringMapping, keys);
final ComboSelectionDialog dialog= new ComboSelectionDialog(getShell(), RefactoringMessages.PushDownInputPage_Edit_members, labelText, keys, initialSelectionIndex);
dialog.setBlockOnOpen(true);
if (dialog.open() == Window.CANCEL)
return;
final int action= stringMapping.get(dialog.getSelectedString()).intValue();
setInfoAction(selectedMembers, action);
} finally {
updateWizardPage(preserved, true);
}
}
private boolean enableEditButton(final IStructuredSelection selection) {
if (selection.isEmpty() || selection.size() == 0)
return false;
return selection.size() == countEditableInfos(getSelectedMemberActionInfos());
}
private MemberActionInfo[] getActiveInfos() {
final MemberActionInfo[] infos= fProcessor.getMemberActionInfos();
final List<MemberActionInfo> result= new ArrayList<>(infos.length);
for (MemberActionInfo info : infos) {
if (info.isActive())
result.add(info);
}
return result.toArray(new MemberActionInfo[result.size()]);
}
private int getCommonActionCodeForSelectedInfos() {
final MemberActionInfo[] infos= getSelectedMemberActionInfos();
if (infos.length == 0)
return -1;
final int code= infos[0].getAction();
for (MemberActionInfo info : infos) {
if (code != info.getAction()) {
return -1;
}
}
return code;
}
private int getInitialSelectionIndexForEditDialog(final Map<String, Integer> mapping, final String[] keys) {
final int commonActionCode= getCommonActionCodeForSelectedInfos();
if (commonActionCode == -1)
return 0;
for (String key : mapping.keySet()) {
final int action= mapping.get(key).intValue();
if (commonActionCode == action) {
for (int index= 0; index < keys.length; index++) {
if (key.equals(keys[index]))
return index;
}
Assert.isTrue(false);// there's no way
}
}
return 0;
}
private IMember[] getMembers() {
final MemberActionInfo[] infos= (MemberActionInfo[]) fTableViewer.getInput();
final List<IMember> result= new ArrayList<>(infos.length);
for (MemberActionInfo info : infos) {
result.add(info.getMember());
}
return result.toArray(new IMember[result.size()]);
}
private MemberActionInfo[] getSelectedMemberActionInfos() {
Assert.isTrue(fTableViewer.getSelection() instanceof IStructuredSelection);
final List<?> result= ((IStructuredSelection) fTableViewer.getSelection()).toList();
return result.toArray(new MemberActionInfo[result.size()]);
}
public void markAdditionalRequiredMembersAsMembersToPushDown() {
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().run(false, false, pm -> {
try {
fProcessor.computeAdditionalRequiredMembersToPushDown(pm);
updateWizardPage(null, true);
} catch (final JavaModelException e) {
throw new InvocationTargetException(e);
} finally {
pm.done();
}
});
} catch (final InvocationTargetException e) {
ExceptionHandler.handle(e, getShell(), RefactoringMessages.PushDownInputPage_Push_Down, RefactoringMessages.PushDownInputPage_Internal_Error);
} catch (final InterruptedException e) {
Assert.isTrue(false);// not cancelable
}
}
private void setActionForMembers(final IMember[] members, final int action) {
final MemberActionInfo[] infos= (MemberActionInfo[]) fTableViewer.getInput();
for (IMember member : members) {
for (MemberActionInfo info : infos) {
if (info.getMember().equals(member)) {
info.setAction(action);
}
}
}
}
private void setupCellEditors(final Table table) {
final ComboBoxCellEditor comboBoxCellEditor= new ComboBoxCellEditor();
comboBoxCellEditor.setStyle(SWT.READ_ONLY);
fTableViewer.setCellEditors(new CellEditor[] { null, comboBoxCellEditor});
fTableViewer.addSelectionChangedListener(event -> {
if (comboBoxCellEditor.getControl() == null && !table.isDisposed())
comboBoxCellEditor.create(table);
Assert.isTrue(event.getSelection() instanceof IStructuredSelection);
final IStructuredSelection ss= (IStructuredSelection) event.getSelection();
if (ss.size() != 1)
return;
final MemberActionInfo mac= (MemberActionInfo) ss.getFirstElement();
comboBoxCellEditor.setItems(MemberActionInfoLabelProvider.getAvailableActionLabels(mac));
comboBoxCellEditor.setValue(Integer.valueOf(mac.getAction()));
});
final ICellModifier cellModifier= new PushDownCellModifier();
fTableViewer.setCellModifier(cellModifier);
fTableViewer.setColumnProperties(new String[] { MEMBER_PROPERTY, ACTION_PROPERTY});
}
@Override
public void setVisible(final boolean visible) {
super.setVisible(visible);
if (visible) {
fTableViewer.setSelection(new StructuredSelection(getActiveInfos()), true);
fTableViewer.getControl().setFocus();
}
}
private void updateButtonEnablementState(final IStructuredSelection tableSelection) {
if (tableSelection == null || fEditButton == null)
return;
fEditButton.setEnabled(enableEditButton(tableSelection));
if (fSelectAllButton != null)
fSelectAllButton.setEnabled(!areAllElementsMarkedAsPushDownAction());
if (fDeselectAllButton != null)
fDeselectAllButton.setEnabled(!areAllElementsMarkedAsNoAction());
}
private void updateStatusLine() {
if (fStatusLine == null)
return;
Object[] selectedMembers= fTableViewer.getCheckedElements();
final int selected= selectedMembers.length;
final String msg= selected == 1 ? Messages.format(RefactoringMessages.PushDownInputPage_status_line_singular, JavaElementLabels.getElementLabel(
((MemberActionInfo)selectedMembers[0]).getMember(), JavaElementLabels.M_PARAMETER_TYPES)) : Messages.format(RefactoringMessages.PushDownInputPage_status_line_plural, String
.valueOf(selected));
fStatusLine.setText(msg);
}
private void updateWizardPage(final ISelection preserved, final boolean displayErrorMessage) {
fTableViewer.refresh();
if (preserved != null) {
fTableViewer.getControl().setFocus();
fTableViewer.setSelection(preserved);
}
checkPageCompletionStatus(displayErrorMessage);
updateButtonEnablementState(((IStructuredSelection) fTableViewer.getSelection()));
updateStatusLine();
}
}
public PushDownWizard(PushDownRefactoringProcessor processor, Refactoring ref) {
super(ref, DIALOG_BASED_USER_INTERFACE);
fProcessor= processor;
setDefaultPageTitle(RefactoringMessages.PushDownWizard_defaultPageTitle);
}
@Override
protected void addUserInputPages() {
addPage(new PushDownInputPage(fProcessor));
}
}