/*
* Copyright (C) 2007, 2009 Martin Kempf, Reto Kleeb, Michael Klenk
*
* IFS Institute for Software, HSR Rapperswil, Switzerland
* http://ifs.hsr.ch/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.refactoring.ui.extract;
import groovyjarjarasm.asm.Opcodes;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.eclipse.refactoring.core.extract.ExtractGroovyMethodRefactoring;
import org.codehaus.groovy.eclipse.refactoring.core.utils.GroovyConventionsBuilder;
import org.codehaus.groovy.eclipse.refactoring.core.utils.StatusHelper;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.Signature;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
/**
* Composite for Userinput for ExtractMethod refactoring
*
* @author Michael Klenk mklenk@hsr.ch
*
*/
public class ExtractMethodPageContent extends Composite implements Observer {
private final ExtractGroovyMethodRefactoring extractMethodRefactoring;
private final ExtractMethodPage extractMethodPage;
private Text txtNewMethodName;
private Text txtPreviewCall;
private Table tblParameters;
private static final String MODIFIER_DEF = "def";
private static final String MODIFIER_PROTECTED = "protected";
private static final String MODIFIER_PRIVATE = "private";
private static final String MODIFIER_NONE = "none";
private final String[] possibleModifiers = { MODIFIER_DEF, MODIFIER_PROTECTED, MODIFIER_PRIVATE, MODIFIER_NONE};
private static final int DEFAULT_MODIFIER = 2;
private Composite accessModifierComposite;
private Composite newMethodNameComposite;
private Composite parameterComposite;
private Composite previewComposite;
private Button btnDown;
private Button btnUp;
private TableEditor editor;
private final Map<String, String> renameVariablesMap = new HashMap<String, String>();
private boolean firstPreviewEver = true;
public ExtractMethodPageContent(Composite parent, ExtractGroovyMethodRefactoring refactoring, ExtractMethodPage extractMethodPage) {
super(parent, SWT.NONE);
this.extractMethodRefactoring = refactoring;
this.extractMethodPage = extractMethodPage;
if (refactoring != null) {
refactoring.addObserver(this);
}
setLayout(new GridLayout());
GridData compositeLData = new GridData();
compositeLData.horizontalAlignment = GridData.FILL;
compositeLData.grabExcessHorizontalSpace = true;
setLayoutData(compositeLData);
createNewMethodNameComposite(this);
createAccessModifierComposite(this);
createParameterComposite(this);
createPreviewComposite(this);
initializeValues(-1);
layout();
}
private void initializeValues(int selectionIndex) {
if (extractMethodRefactoring != null) {
setModifier();
if(extractMethodRefactoring.getCallAndMethHeadParameters().length > 0){
tblParameters.removeAll();
for (Parameter param : extractMethodRefactoring.getCallAndMethHeadParameters()) {
TableItem tblItem = new TableItem(tblParameters, SWT.NONE);
tblItem.setText(0, createSimpleTypeName(param.getType()));
setVariableNameInTable(param, tblItem);
}
tblParameters.setSelection(selectionIndex);
updateButtonsEnabled(tblParameters);
}
updateView();
}
}
private String createSimpleTypeName(ClassNode node) {
String name = node.getName();
if (name.startsWith("[")) {
int arrayCount = Signature.getArrayCount(name);
String noArrayName = Signature.getElementType(name);
String simpleName = Signature.getSignatureSimpleName(noArrayName);
StringBuilder sb = new StringBuilder();
sb.append(simpleName);
for (int i = 0; i < arrayCount; i++) {
sb.append("[]");
}
return sb.toString();
} else {
return node.getNameWithoutPackage();
}
}
private void setVariableNameInTable(Parameter param, TableItem tblItem) {
String variableName = param.getName();
if(renameVariablesMap.containsKey(variableName))
tblItem.setText(1, renameVariablesMap.get(variableName));
else
tblItem.setText(1, variableName);
}
/**
* Update View and check the given Values
*/
private void updateView() {
String methodHead = extractMethodRefactoring.getMethodHead();
if(firstPreviewEver){
createDummyMethodHead(methodHead);
}else{
txtPreviewCall.setText(methodHead);
}
RefactoringStatus status = validateGroovyIdentifiers();
checkForDuplicateVariableNames(status);
extractMethodPage.setPageComplete(status);
}
private void checkForDuplicateVariableNames(RefactoringStatus status) {
HashSet<String> uniquenessTestSet = new HashSet<String>();
for (Parameter p : extractMethodRefactoring.getCallAndMethHeadParameters()){
if(!uniquenessTestSet.add(p.getName())){
String errorMsg = MessageFormat.format(GroovyRefactoringMessages.ExtractMethodWizard_DuplicateVariableName, p.getName());
status.addFatalError(errorMsg);
}
}
}
private void createDummyMethodHead(String methodHead) {
String dummyMethodName = GroovyRefactoringMessages.ExtractMethodWizard_DefaultMethodName;
int paraStartPos = methodHead.indexOf('(');
String partOne = methodHead.substring(0, paraStartPos);
String partTwo = methodHead.substring(paraStartPos,methodHead.length());
txtPreviewCall.setText(partOne + dummyMethodName + partTwo);
}
private RefactoringStatus validateGroovyIdentifiers() {
List<String> variablesToCheck = new LinkedList<String>();
variablesToCheck.add(txtNewMethodName.getText());
for(Parameter p : extractMethodRefactoring.getCallAndMethHeadParameters()){
variablesToCheck.add(p.getName());
}
IStatus statusOfUsedGroovyIdentifiers = new GroovyConventionsBuilder(variablesToCheck, GroovyConventionsBuilder.METHOD)
.validateGroovyIdentifier().validateLowerCase(IStatus.WARNING).done();
return StatusHelper.convertStatus(statusOfUsedGroovyIdentifiers);
}
private void setModifier() {
extractMethodRefactoring.setModifier(DEFAULT_MODIFIER);
}
private void setModifier(MouseEvent e) {
if(e.getSource() instanceof Button){
Button selectedButton = (Button) e.getSource();
if (selectedButton.getText().equals(MODIFIER_PRIVATE))
extractMethodRefactoring.setModifier(Opcodes.ACC_PRIVATE);
else if (selectedButton.getText().equals(MODIFIER_DEF))
extractMethodRefactoring.setModifier(Opcodes.ACC_PUBLIC);
else if (selectedButton.getText().equals(MODIFIER_PROTECTED))
extractMethodRefactoring.setModifier(Opcodes.ACC_PROTECTED);
else if (selectedButton.getText().equals(MODIFIER_NONE))
extractMethodRefactoring.setModifier(0);
}
}
public void update(Observable o, Object arg) {
updateView();
}
private void createAccessModifierComposite(Composite parent) {
accessModifierComposite = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 6;
gridLayout.marginHeight = 0;
GridData compositeLData = new GridData();
compositeLData.horizontalAlignment = GridData.FILL;
compositeLData.grabExcessHorizontalSpace = true;
accessModifierComposite.setLayoutData(compositeLData);
accessModifierComposite.setLayout(gridLayout);
Label lbAccessModifier = new Label(accessModifierComposite, SWT.NONE);
lbAccessModifier.setText(GroovyRefactoringMessages.ExtractMethodWizard_LB_AcessModifier);
GridData data = new GridData();
data.horizontalSpan = 2;
lbAccessModifier.setLayoutData(data);
MouseAdapter btnClick = new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
super.mouseUp(e);
setModifier(e);
}
};
createRadioButtons(btnClick);
}
private void createRadioButtons(MouseAdapter btnClick) {
for(String buttonName : possibleModifiers){
Button btnModifier = new Button(accessModifierComposite, SWT.RADIO);
btnModifier.setText(buttonName);
btnModifier.addMouseListener(btnClick);
enableAndSelectButtons(buttonName, btnModifier);
}
}
private void enableAndSelectButtons(String buttonName, Button btnModifier) {
if(buttonName.equals(possibleModifiers[DEFAULT_MODIFIER])){
btnModifier.setSelection(true);
}
if(buttonName.equals(MODIFIER_NONE)){
btnModifier.setEnabled(extractMethodRefactoring.isStatic());
}
}
private void createNewMethodNameComposite(Composite parent) {
newMethodNameComposite = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 2;
gridLayout.marginHeight = 10;
GridData compositeLData = new GridData();
compositeLData.horizontalAlignment = GridData.FILL;
compositeLData.grabExcessHorizontalSpace = true;
newMethodNameComposite.setLayoutData(compositeLData);
newMethodNameComposite.setLayout(gridLayout);
Label lbMethodName = new Label(newMethodNameComposite, SWT.NONE);
lbMethodName.setText(GroovyRefactoringMessages.ExtractMethodWizard_LB_NewMethodName);
txtNewMethodName = new Text(newMethodNameComposite, SWT.BORDER);
txtNewMethodName.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
firstPreviewEver = false;
extractMethodRefactoring.setNewMethodname(txtNewMethodName.getText());
}
});
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.grabExcessHorizontalSpace = true;
txtNewMethodName.setLayoutData(data);
}
private void createParameterComposite(Composite parent) {
if(extractMethodRefactoring.getCallAndMethHeadParameters().length > 0){
GridLayout gridLayout;
initParameterComposite(parent);
Label lbParameters = new Label(parameterComposite, SWT.NONE);
lbParameters.setText(GroovyRefactoringMessages.ExtractMethodWizard_LB_Parameters);
Composite parameterTableAndButtons = createTableAndButtonComposite();
Composite tableframe = createTableComposite(parameterTableAndButtons);
createParameterTable(tableframe);
Composite buttonframe = new Composite(parameterTableAndButtons, SWT.NONE);
gridLayout = new GridLayout();
gridLayout.numColumns = 1;
buttonframe.setLayout(gridLayout);
GridData buttonData = new GridData();
buttonData.widthHint = 90;
createUpButton(buttonframe, buttonData);
createDownButton(buttonframe, buttonData);
createTableSelectionListener();
}
}
private void createTableSelectionListener() {
createTableEditor();
tblParameters.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
final int EDITABLECOLUMN = 1;// editing the second column
TableItem currentTableItem = ((TableItem)e.item);
if (currentTableItem == null) return;
updateButtonsEnabled(tblParameters);
disposeTableEditor();
Text newEditor = new Text(tblParameters, SWT.NONE);
newEditor.setText(currentTableItem.getText(EDITABLECOLUMN));
addListenerToEditor(EDITABLECOLUMN, newEditor);
newEditor.selectAll();
newEditor.setFocus();
editor.setEditor(newEditor, currentTableItem, EDITABLECOLUMN);
}
private void addListenerToEditor(final int EDITABLECOLUMN, Text newEditor) {
newEditor.addModifyListener(new ModifyListener(){
public void modifyText(ModifyEvent e) {
saveRenamedVariable(EDITABLECOLUMN);
}
private void saveRenamedVariable(final int EDITABLECOLUMN) {
Text text = (Text) editor.getEditor();
int selectionIndex = tblParameters.getSelectionIndex();
String before = extractMethodRefactoring.getOriginalParameterName(selectionIndex);
editor.getItem().setText(EDITABLECOLUMN, text.getText());
String after = editor.getItem().getText(EDITABLECOLUMN);
renameVariablesMap.put(before, after);
extractMethodRefactoring.setParameterRename(renameVariablesMap);
}
});
}
});
}
private void createTableEditor() {
editor = new TableEditor(tblParameters);
editor.horizontalAlignment = SWT.LEFT;
editor.grabHorizontal = true;
editor.minimumWidth = 200;
}
private Button createPushButton(Composite buttonframe, GridData buttonData, String text, boolean enabled, boolean visible) {
Button pushButton = new Button(buttonframe, SWT.PUSH);
pushButton.setText(text);
pushButton.setLayoutData(buttonData);
pushButton.setEnabled(enabled);
pushButton.setVisible(visible);
return pushButton;
}
private void createDownButton(Composite buttonframe, GridData buttonData) {
btnDown = createPushButton(buttonframe, buttonData, GroovyRefactoringMessages.ExtractMethodWizard_LB_BTN_Down, false, true);
btnDown.addMouseListener(new ExtractMethodMouseAdapter(this,ExtractMethodMouseAdapter.DOWNEVENT));
}
private void createUpButton(Composite buttonframe, GridData buttonData) {
btnUp = createPushButton(buttonframe, buttonData, GroovyRefactoringMessages.ExtractMethodWizard_LB_BTN_UP, false, true);
btnUp.addMouseListener(new ExtractMethodMouseAdapter(this,ExtractMethodMouseAdapter.UPEVENT));
}
private void createParameterTable(Composite tableframe) {
int tableParams = SWT.BORDER | SWT.V_SCROLL | SWT.FULL_SELECTION;
tblParameters = new Table(tableframe, tableParams);
tblParameters.setLinesVisible(true);
tblParameters.setHeaderVisible(true);
createTableColumn(GroovyRefactoringMessages.ExtractMethodWizard_LB_Col_Type);
createTableColumn(GroovyRefactoringMessages.ExtractMethodWizard_LB_Col_Name);
}
private Composite createTableComposite(Composite parameterTableAndButtons) {
GridLayout gridLayout;
Composite tableframe = new Composite(parameterTableAndButtons, SWT.NONE);
gridLayout = new GridLayout();
gridLayout.numColumns = 1;
gridLayout.horizontalSpacing = 0;
tableframe.setLayout(gridLayout);
return tableframe;
}
private Composite createTableAndButtonComposite() {
GridLayout gridLayout;
Composite parameterTableAndButtons = new Composite(parameterComposite, SWT.NONE);
gridLayout = new GridLayout();
gridLayout.numColumns = 2;
parameterTableAndButtons.setLayout(gridLayout);
return parameterTableAndButtons;
}
private void initParameterComposite(Composite parent) {
parameterComposite = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 1;
gridLayout.marginHeight = 0;
gridLayout.marginTop = 10;
gridLayout.verticalSpacing = 0;
GridData compositeLData = new GridData();
compositeLData.horizontalAlignment = GridData.FILL;
compositeLData.grabExcessHorizontalSpace = true;
parameterComposite.setLayoutData(compositeLData);
parameterComposite.setLayout(gridLayout);
}
private void createTableColumn(String title) {
TableColumn column = new TableColumn(tblParameters, SWT.NONE);
column.setText(title);
column.setWidth(200);
column.setMoveable(false);
column.setResizable(false);
}
private void createPreviewComposite(Composite parent) {
previewComposite = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 0;
GridData compositeLData = new GridData();
compositeLData.horizontalAlignment = GridData.FILL;
compositeLData.grabExcessHorizontalSpace = true;
previewComposite.setLayoutData(compositeLData);
previewComposite.setLayout(gridLayout);
Label lbPreview = new Label(previewComposite, SWT.NONE);
lbPreview.setText(GroovyRefactoringMessages.ExtractMethodWizard_LB_MethodSignaturePreview);
txtPreviewCall = new Text(previewComposite, SWT.MULTI | SWT.READ_ONLY);
txtPreviewCall.setText(GroovyRefactoringMessages.ExtractMethodWizard_MethodCall);
GridData data = new GridData();
data.heightHint = 60;
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
txtPreviewCall.setLayoutData(data);
}
protected void handleUpDownEvent(boolean upEvent) {
String variName = "";
if (tblParameters.getSelectionCount() > 0){
variName = tblParameters.getSelection()[0].getText(1);
}
int indexOfSelectedParam = extractMethodRefactoring.setMoveParameter(variName, upEvent, 1);
initializeValues(indexOfSelectedParam);
disposeTableEditor();
}
private void disposeTableEditor() {
Control oldEditor = editor.getEditor();
if (oldEditor != null) oldEditor.dispose();
}
private void updateButtonsEnabled(Table tbl) {
int lastElementIndex = extractMethodRefactoring.getCallAndMethHeadParameters().length - 1;
int selectionIndex = tbl.getSelectionIndex();
if(tbl.getItemCount() == 1){
btnDown.setEnabled(false);
btnUp.setEnabled(false);
} else if(selectionIndex == 0){
btnDown.setEnabled(true);
btnUp.setEnabled(false);
} else if(selectionIndex == lastElementIndex){
btnDown.setEnabled(false);
btnUp.setEnabled(true);
} else {
btnDown.setEnabled(true);
btnUp.setEnabled(true);
}
}
}
class ExtractMethodMouseAdapter extends MouseAdapter{
public static final int EDITEVENT = 1;
public static final int DOWNEVENT = 2;
public static final int UPEVENT = 3;
private final ExtractMethodPageContent wizard;
private final int eventType;
public ExtractMethodMouseAdapter(ExtractMethodPageContent wizard, int eventtype){
this.wizard = wizard;
this.eventType = eventtype;
}
@Override
public void mouseUp(MouseEvent e) {
super.mouseUp(e);
switch(eventType){
case DOWNEVENT:
wizard.handleUpDownEvent(false);
break;
case UPEVENT:
wizard.handleUpDownEvent(true);
break;
}
}
}