blob: fc933de2b80ee0842148be3ef0e9cfdd77217459 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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 com.intellij.refactoring.extractMethod;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.ui.ComboBoxVisibilityPanel;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.ui.JavaComboBoxVisibilityPanel;
import com.intellij.refactoring.ui.MethodSignatureComponent;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.ParameterTablePanel;
import com.intellij.refactoring.util.VariableData;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.NonFocusableCheckBox;
import com.intellij.ui.SeparatorFactory;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
/**
* @author Konstantin Bulenkov
*/
@SuppressWarnings("MethodMayBeStatic")
public class ExtractMethodDialog extends DialogWrapper implements AbstractExtractDialog {
public static final String EXTRACT_METHOD_DEFAULT_VISIBILITY = "extract.method.default.visibility";
private final Project myProject;
private final PsiType myReturnType;
private final PsiTypeParameterList myTypeParameterList;
private final PsiType[] myExceptions;
private final boolean myStaticFlag;
private boolean myCanBeStatic;
private final PsiElement[] myElementsToExtract;
private final String myHelpId;
private final EditorTextField myNameField;
private final MethodSignatureComponent mySignature;
private final JCheckBox myMakeStatic;
protected JCheckBox myMakeVarargs;
private JCheckBox myCbChainedConstructor;
private final InputVariables myVariableData;
private final PsiClass myTargetClass;
private ComboBoxVisibilityPanel<String> myVisibilityPanel;
private boolean myDefaultVisibility = true;
private boolean myChangingVisibility;
private final JCheckBox myFoldParameters = new NonFocusableCheckBox(RefactoringBundle.message("declare.folded.parameters"));
public JPanel myCenterPanel;
public JPanel myParamTable;
private VariableData[] myInputVariables;
public ExtractMethodDialog(Project project,
PsiClass targetClass, final InputVariables inputVariables, PsiType returnType,
PsiTypeParameterList typeParameterList, PsiType[] exceptions, boolean isStatic, boolean canBeStatic,
final boolean canBeChainedConstructor,
String initialMethodName,
String title,
String helpId,
final PsiElement[] elementsToExtract) {
super(project, true);
myProject = project;
myTargetClass = targetClass;
myReturnType = returnType;
myTypeParameterList = typeParameterList;
myExceptions = exceptions;
myStaticFlag = isStatic;
myCanBeStatic = canBeStatic;
myElementsToExtract = elementsToExtract;
myVariableData = inputVariables;
myHelpId = helpId;
mySignature = new MethodSignatureComponent("", project, JavaFileType.INSTANCE);
mySignature.setPreferredSize(new Dimension(500, 100));
mySignature.setMinimumSize(new Dimension(500, 100));
setTitle(title);
// Create UI components
myNameField = createNameField(initialMethodName);
int height = myVariableData.getInputVariables().size() + 2;
if (myExceptions.length > 0) {
height += myExceptions.length + 1;
}
myMakeStatic = new NonFocusableCheckBox();
myMakeStatic.setText(RefactoringBundle.message("declare.static.checkbox"));
if (canBeChainedConstructor) {
myCbChainedConstructor = new NonFocusableCheckBox(RefactoringBundle.message("extract.chained.constructor.checkbox"));
}
init();
}
protected EditorTextField createNameField(String initialMethodName) {
EditorTextField field = new EditorTextField(initialMethodName, myProject, StdFileTypes.JAVA);
field.selectAll();
return field;
}
protected boolean areTypesDirected() {
return true;
}
public boolean isMakeStatic() {
if (myStaticFlag) return true;
return myCanBeStatic && myMakeStatic.isSelected();
}
public boolean isChainedConstructor() {
return myCbChainedConstructor != null && myCbChainedConstructor.isSelected();
}
@NotNull
protected Action[] createActions() {
if (myHelpId != null) {
return new Action[]{getOKAction(), getCancelAction(), getHelpAction()};
} else {
return new Action[]{getOKAction(), getCancelAction()};
}
}
public String getChosenMethodName() {
return myNameField.getText();
}
public VariableData[] getChosenParameters() {
return myInputVariables;
}
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected String getHelpId() {
return myHelpId;
}
protected void doOKAction() {
MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
checkMethodConflicts(conflicts);
if (!conflicts.isEmpty()) {
final ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts);
conflictsDialog.show();
if (!conflictsDialog.isOK()){
if (conflictsDialog.isShowConflicts()) close(CANCEL_EXIT_CODE);
return;
}
}
if (myMakeVarargs != null && myMakeVarargs.isSelected()) {
final VariableData data = myInputVariables[myInputVariables.length - 1];
if (data.type instanceof PsiArrayType) {
data.type = new PsiEllipsisType(((PsiArrayType)data.type).getComponentType());
}
}
final PsiMethod containingMethod = getContainingMethod();
if (containingMethod != null && containingMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
PropertiesComponent.getInstance(myProject).setValue(EXTRACT_METHOD_DEFAULT_VISIBILITY, getVisibility());
}
super.doOKAction();
}
protected JComponent createNorthPanel() {
final JPanel main = new JPanel(new BorderLayout());
final JPanel namePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 2, true, false));
final JLabel nameLabel = new JLabel();
nameLabel.setText(RefactoringBundle.message("changeSignature.name.prompt"));
namePanel.add(nameLabel);
namePanel.add(myNameField);
nameLabel.setLabelFor(myNameField);
myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
public void documentChanged(DocumentEvent e) {
update();
}
});
myVisibilityPanel = createVisibilityPanel();
final JPanel visibilityAndName = new JPanel(new BorderLayout(2, 0));
visibilityAndName.add(myVisibilityPanel, BorderLayout.WEST);
visibilityAndName.add(namePanel, BorderLayout.CENTER);
main.add(visibilityAndName, BorderLayout.CENTER);
setOKActionEnabled(false);
setOKActionEnabled(PsiNameHelper.getInstance(myProject).isIdentifier(myNameField.getText()));
final JPanel options = new JPanel(new BorderLayout());
options.add(createOptionsPanel(), BorderLayout.WEST);
main.add(options, BorderLayout.SOUTH);
return main;
}
protected JPanel createOptionsPanel() {
final JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5));
//optionsPanel.add(new JLabel("Options: "));
if (myStaticFlag || myCanBeStatic) {
myMakeStatic.setEnabled(!myStaticFlag);
myMakeStatic.setSelected(myStaticFlag);
myMakeStatic.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
updateSignature();
}
});
optionsPanel.add(myMakeStatic);
} else {
myMakeStatic.setSelected(false);
myMakeStatic.setEnabled(false);
}
final Border emptyBorder = IdeBorderFactory.createEmptyBorder(5, 0, 5, 4);
myMakeStatic.setBorder(emptyBorder);
myFoldParameters.setSelected(myVariableData.isFoldingSelectedByDefault());
myFoldParameters.setVisible(myVariableData.isFoldable());
myVariableData.setFoldingAvailable(myFoldParameters.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
myFoldParameters.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myVariableData.setFoldingAvailable(myFoldParameters.isSelected());
myInputVariables =
myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
updateVarargsEnabled();
createParametersPanel();
updateSignature();
}
});
optionsPanel.add(myFoldParameters);
myFoldParameters.setBorder(emptyBorder);
boolean canBeVarargs = false;
for (VariableData data : myInputVariables) {
canBeVarargs |= data.type instanceof PsiArrayType;
}
if (myVariableData.isFoldable()) {
canBeVarargs |= myVariableData.isFoldingSelectedByDefault();
}
if (canBeVarargs) {
myMakeVarargs = new NonFocusableCheckBox(RefactoringBundle.message("declare.varargs.checkbox"));
myMakeVarargs.setBorder(emptyBorder);
updateVarargsEnabled();
myMakeVarargs.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
updateSignature();
}
});
myMakeVarargs.setSelected(false);
optionsPanel.add(myMakeVarargs);
}
if (myCbChainedConstructor != null) {
optionsPanel.add(myCbChainedConstructor);
myCbChainedConstructor.setBorder(emptyBorder);
myCbChainedConstructor.addItemListener(new ItemListener() {
public void itemStateChanged(final ItemEvent e) {
if (myDefaultVisibility) {
myChangingVisibility = true;
try {
if (isChainedConstructor()) {
myVisibilityPanel.setVisibility(VisibilityUtil.getVisibilityModifier(myTargetClass.getModifierList()));
}
else {
myVisibilityPanel.setVisibility(PsiModifier.PRIVATE);
}
}
finally {
myChangingVisibility = false;
}
}
update();
}
});
}
return optionsPanel;
}
private ComboBoxVisibilityPanel<String> createVisibilityPanel() {
final JavaComboBoxVisibilityPanel panel = new JavaComboBoxVisibilityPanel();
final PsiMethod containingMethod = getContainingMethod();
panel.setVisibility(containingMethod != null && containingMethod.hasModifierProperty(PsiModifier.PUBLIC)
? PropertiesComponent.getInstance(myProject).getOrInit( EXTRACT_METHOD_DEFAULT_VISIBILITY, PsiModifier.PRIVATE)
: PsiModifier.PRIVATE);
panel.addListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
updateSignature();
if (!myChangingVisibility) {
myDefaultVisibility = false;
}
}
});
return panel;
}
private PsiMethod getContainingMethod() {
return PsiTreeUtil.getParentOfType(PsiTreeUtil.findCommonParent(myElementsToExtract), PsiMethod.class);
}
private void updateVarargsEnabled() {
if (myMakeVarargs != null) {
myMakeVarargs.setEnabled(myInputVariables[myInputVariables.length - 1].type instanceof PsiArrayType);
}
}
private void update() {
myNameField.setEnabled(!isChainedConstructor());
if (myMakeStatic != null) {
myMakeStatic.setEnabled(!myStaticFlag && myCanBeStatic && !isChainedConstructor());
}
updateSignature();
setOKActionEnabled(PsiNameHelper.getInstance(myProject).isIdentifier(myNameField.getText()) ||
isChainedConstructor());
}
public String getVisibility() {
return myVisibilityPanel.getVisibility();
}
protected JComponent createCenterPanel() {
myCenterPanel = new JPanel(new BorderLayout());
createParametersPanel();
final Splitter splitter = new Splitter(true);
splitter.setShowDividerIcon(false);
splitter.setFirstComponent(myCenterPanel);
splitter.setSecondComponent(createSignaturePanel());
return splitter;
}
protected boolean isOutputVariable(PsiVariable var) {
return false;
}
protected void createParametersPanel() {
if (myParamTable != null) {
myCenterPanel.remove(myParamTable);
}
myParamTable = createParameterTableComponent();
myParamTable.setMinimumSize(new Dimension(500, 100));
myCenterPanel.add(myParamTable, BorderLayout.CENTER);
final JTable table = UIUtil.findComponentOfType(myParamTable, JTable.class);
myCenterPanel.add(SeparatorFactory.createSeparator("&Parameters", table), BorderLayout.NORTH);
if (table != null) {
table.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (table.getRowCount() > 0) {
final int col = table.getSelectedColumn();
final int row = table.getSelectedRow();
if (col == -1 || row == -1) {
table.getSelectionModel().setSelectionInterval(0, 0);
table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0);
}
}
}
});
}
}
protected ParameterTablePanel createParameterTableComponent() {
return new ParameterTablePanel(myProject, myInputVariables, myElementsToExtract) {
protected void updateSignature() {
updateVarargsEnabled();
ExtractMethodDialog.this.updateSignature();
}
protected void doEnterAction() {
clickDefaultButton();
}
protected void doCancelAction() {
ExtractMethodDialog.this.doCancelAction();
}
protected boolean areTypesDirected() {
return ExtractMethodDialog.this.areTypesDirected();
}
@Override
protected boolean isUsedAfter(PsiVariable variable) {
return isOutputVariable(variable);
}
};
}
protected JComponent createSignaturePanel() {
final JPanel panel = new JPanel(new BorderLayout());
panel.add(SeparatorFactory.createSeparator(RefactoringBundle.message("signature.preview.border.title"), null), BorderLayout.NORTH);
panel.add(mySignature, BorderLayout.CENTER);
updateSignature();
return panel;
}
protected void updateSignature() {
if (mySignature != null) {
mySignature.setSignature(getSignature());
}
}
protected String getSignature() {
final @NonNls StringBuilder buffer = new StringBuilder();
final String visibilityString = VisibilityUtil.getVisibilityString(myVisibilityPanel.getVisibility());
buffer.append(visibilityString);
if (buffer.length() > 0) {
buffer.append(" ");
}
if (isMakeStatic() && !isChainedConstructor()) {
buffer.append("static ");
}
if (myTypeParameterList != null) {
final String typeParamsText = myTypeParameterList.getText();
if (!typeParamsText.isEmpty()) {
buffer.append(typeParamsText);
buffer.append(" ");
}
}
if (isChainedConstructor()) {
buffer.append(myTargetClass.getName());
}
else {
buffer.append(PsiFormatUtil.formatType(myReturnType, 0, PsiSubstitutor.EMPTY));
buffer.append(" ");
buffer.append(myNameField.getText());
}
buffer.append("(");
final String INDENT = StringUtil.repeatSymbol(' ', buffer.length());
final VariableData[] datas = myInputVariables;
int count = 0;
for (int i = 0; i < datas.length;i++) {
VariableData data = datas[i];
if (data.passAsParameter) {
//String typeAndModifiers = PsiFormatUtil.formatVariable(data.variable,
// PsiFormatUtil.SHOW_MODIFIERS | PsiFormatUtil.SHOW_TYPE);
PsiType type = data.type;
if (i == datas.length - 1 && type instanceof PsiArrayType && myMakeVarargs != null && myMakeVarargs.isSelected()) {
type = new PsiEllipsisType(((PsiArrayType)type).getComponentType());
}
String typeText = type.getPresentableText();
if (count > 0) {
buffer.append(",\n");
buffer.append(INDENT);
}
buffer.append(typeText);
buffer.append(" ");
buffer.append(data.name);
count++;
}
}
buffer.append(")");
if (myExceptions.length > 0) {
buffer.append("\n");
buffer.append("throws\n");
for (PsiType exception : myExceptions) {
buffer.append(INDENT);
buffer.append(PsiFormatUtil.formatType(exception, 0, PsiSubstitutor.EMPTY));
buffer.append("\n");
}
}
return buffer.toString();
}
@Override
protected String getDimensionServiceKey() {
return "extract.method.dialog";
}
protected void checkMethodConflicts(MultiMap<PsiElement, String> conflicts) {
PsiMethod prototype;
try {
PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory();
prototype = factory.createMethod(myNameField.getText().trim(), myReturnType);
if (myTypeParameterList != null) prototype.getTypeParameterList().replace(myTypeParameterList);
for (VariableData data : myInputVariables) {
if (data.passAsParameter) {
prototype.getParameterList().add(factory.createParameter(data.name, data.type));
}
}
// set the modifiers with which the method is supposed to be created
PsiUtil.setModifierProperty(prototype, PsiModifier.PRIVATE, true);
} catch (IncorrectOperationException e) {
return;
}
ConflictsUtil.checkMethodConflicts(myTargetClass, null, prototype, conflicts);
}
}