introduce parameter dialog

This commit is contained in:
Maxim.Medvedev
2012-02-09 16:44:56 +04:00
parent 040cd95ebe
commit e5f09509af
9 changed files with 319 additions and 507 deletions
@@ -167,3 +167,4 @@ you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method=You cannot
rename.groovy.implicit.closure.parameter=Rename implicit closure parameter
rename.implicit.closure.parameter.to=Rename implicit closure parameter to\:
implicit.closure.parameter=Implicit closure parameter
selected.expression.should.not.be.lvalue=Selected expression should not be left hand side of assignment
@@ -36,6 +36,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
@@ -49,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.refactoring.GrRefactoringError;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.inline.GroovyInlineMethodUtil;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase;
import java.util.ArrayList;
import java.util.Collection;
@@ -107,6 +109,13 @@ public class GroovyExtractChooser {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.expression.has.void.type"));
}
if (ExtractUtil.isSingleExpression(statements) && GrIntroduceHandlerBase.expressionIsIncorrect((GrExpression)statement0)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.block.should.represent.an.expression"));
}
if (ExtractUtil.isSingleExpression(statements) && statement0.getParent() instanceof GrAssignmentExpression && ((GrAssignmentExpression)statement0.getParent()).getLValue()==statement0) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.expression.should.not.be.lvalue"));
}
// collect information about return statements in selected statement set
@@ -1,255 +0,0 @@
/*
* 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 org.jetbrains.plugins.groovy.refactoring.extract.closure;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.refactoring.IntroduceParameterRefactoring;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntArrayList;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterSettings;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GroovyIntroduceParameterUtil;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.IntroduceParameterInfo;
import org.jetbrains.plugins.groovy.refactoring.ui.GrMethodSignatureComponent;
import javax.swing.*;
import java.awt.*;
/**
* @author Max Medvedev
*/
public class ExtractClosureDialog extends DialogWrapper {
private final ParameterTablePanel myTable;
private final GrMethodSignatureComponent mySignature;
private final EditorTextField myNameField;
private final IntroduceParameterInfo myInfo;
private final JBCheckBox myFinalCB;
private final JBCheckBox myGenerateDelegateCB;
TObjectIntHashMap<JBCheckBox> toRemoveCBs;
public ExtractClosureDialog(IntroduceParameterInfo initialInfo) {
super(initialInfo.getProject());
myInfo = initialInfo;
setTitle("Extract closure");
myTable = new ParameterTablePanel() {
@Override
protected void updateSignature() {
ExtractClosureDialog.this.updateSignature();
}
@Override
protected void doEnterAction() {
clickDefaultButton();
}
@Override
protected void doCancelAction() {
ExtractClosureDialog.this.doCancelAction();
}
};
mySignature = new GrMethodSignatureComponent("", initialInfo.getProject());
myNameField = new EditorTextField("", initialInfo.getProject(), GroovyFileType.GROOVY_FILE_TYPE);
myFinalCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("Declare &final"));
myFinalCB.setFocusable(false);
myGenerateDelegateCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("De&legate via overloading method"));
myGenerateDelegateCB.setFocusable(false);
TObjectIntHashMap<GrParameter> parametersToRemove = GroovyIntroduceParameterUtil.findParametersToRemove(myInfo);
toRemoveCBs = new TObjectIntHashMap<JBCheckBox>(parametersToRemove.size());
for (Object p : parametersToRemove.keys()) {
JBCheckBox cb = new JBCheckBox(GroovyRefactoringBundle.message("remove.parameter.0.no.longer.used", ((GrParameter)p).getName()));
cb.setFocusable(false);
toRemoveCBs.put(cb, parametersToRemove.get((GrParameter)p));
}
init();
}
@Override
protected ValidationInfo doValidate() {
final String text = myNameField.getText().trim();
if (!StringUtil.isJavaIdentifier(text)) {
return new ValidationInfo(GroovyRefactoringBundle.message("name.is.wrong", text), myNameField);
}
final Ref<ValidationInfo> info = new Ref<ValidationInfo>();
toRemoveCBs.forEachEntry(new TObjectIntProcedure<JBCheckBox>() {
@Override
public boolean execute(JBCheckBox checkbox, int index) {
if (!checkbox.isSelected()) return true;
final GrParameter param = myInfo.getToReplaceIn().getParameters()[index];
final ParameterInfo pinfo = findParamByOldName(param.getName());
if (pinfo == null || !pinfo.passAsParameter()) return true;
final String message = GroovyRefactoringBundle.message("you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method",
pinfo.getName(), param.getName());
info.set(new ValidationInfo(message));
return false;
}
});
return info.get();
}
@Nullable
private ParameterInfo findParamByOldName(String name) {
for (ParameterInfo info : myInfo.getParameterInfos()) {
if (name.equals(info.getOldName())) return info;
}
return null;
}
private void updateSignature() {
StringBuilder b = new StringBuilder();
b.append("{ ");
String[] params = ExtractUtil.getParameterString(myInfo, false);
for (int i = 0; i < params.length; i++) {
if (i > 0) {
b.append(" ");
}
b.append(params[i]);
b.append('\n');
}
b.append(" ->\n}");
mySignature.setSignature(b.toString());
}
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected void init() {
super.init();
myTable.init(myInfo);
final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
final Boolean settingsFinals = refactoringSettings.INTRODUCE_PARAMETER_CREATE_FINALS;
myFinalCB.setSelected(settingsFinals == null ?
CodeStyleSettingsManager.getSettings(myInfo.getProject()).GENERATE_FINAL_PARAMETERS :
settingsFinals.booleanValue());
myGenerateDelegateCB.setSelected(false);
final GrParameter[] parameters = myInfo.getToReplaceIn().getParameters();
toRemoveCBs.forEachEntry(new TObjectIntProcedure<JBCheckBox>() {
@Override
public boolean execute(JBCheckBox checkbox, int index) {
checkbox.setSelected(true);
final GrParameter param = parameters[index];
final ParameterInfo pinfo = findParamByOldName(param.getName());
if (pinfo != null) {
pinfo.setPassAsParameter(false);
}
return true;
}
});
updateSignature();
}
@Override
protected JComponent createNorthPanel() {
final JPanel panel = new JPanel(new BorderLayout());
final JBLabel nameLabel = new JBLabel();
panel.add(nameLabel, BorderLayout.NORTH);
panel.add(myNameField, BorderLayout.CENTER);
nameLabel.setText(UIUtil.replaceMnemonicAmpersand("Parameter &name:"));
nameLabel.setLabelFor(myNameField);
final JPanel checkBoxPanel = new JPanel();
checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.Y_AXIS));
checkBoxPanel.add(myFinalCB);
checkBoxPanel.add(myGenerateDelegateCB);
for (Object cb : toRemoveCBs.keys()) {
checkBoxPanel.add((Component)cb);
}
panel.add(checkBoxPanel, BorderLayout.SOUTH);
return panel;
}
@Override
protected JComponent createCenterPanel() {
final JPanel signaturePanel = new JPanel(new BorderLayout());
signaturePanel.setBorder(
IdeBorderFactory.createTitledBorder(GroovyRefactoringBundle.message("signature.preview.border.title"), false, false, true));
signaturePanel.add(mySignature, BorderLayout.CENTER);
Splitter splitter = new Splitter(true);
splitter.setFirstComponent(myTable);
splitter.setSecondComponent(signaturePanel);
mySignature.setPreferredSize(new Dimension(500, 100));
mySignature.setSize(new Dimension(500, 100));
splitter.setShowDividerIcon(false);
return splitter;
}
@Override
protected void doOKAction() {
final JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
settings.INTRODUCE_PARAMETER_CREATE_FINALS = Boolean.valueOf(myFinalCB.isSelected());
super.doOKAction();
}
public GrIntroduceParameterSettings getHelper() {
TIntArrayList list = new TIntArrayList();
for (Object cb : toRemoveCBs.keys()) {
final JBCheckBox checkbox = (JBCheckBox)cb;
if (checkbox.isSelected()) {
list.add(toRemoveCBs.get(checkbox));
}
}
return new ExtractClosureHelperImpl(myInfo,
myNameField.getText(),
myFinalCB.isSelected(),
list,
myGenerateDelegateCB.isSelected(),
IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE
);
}
}
@@ -225,7 +225,7 @@ public class ExtractClosureFromMethodProcessor extends ExtractClosureProcessorBa
private final PsiType myType;
private IntroduceParameterDataAdapter() {
myClosure = generateClosure();
myClosure = generateClosure(ExtractClosureFromMethodProcessor.this.myHelper);
myWrapper = new GrExpressionWrapper(myClosure);
PsiType type = myClosure.getType();
@@ -60,25 +60,25 @@ public abstract class ExtractClosureProcessorBase extends BaseRefactoringProcess
return EXTRACT_CLOSURE;
}
protected GrClosableBlock generateClosure() {
public static GrClosableBlock generateClosure(GrIntroduceParameterSettings helper) {
StringBuilder buffer = new StringBuilder();
buffer.append('{');
final String[] params = ExtractUtil.getParameterString(myHelper, true);
final String[] params = ExtractUtil.getParameterString(helper, true);
if (params.length > 0) {
for (String p : params) {
buffer.append(p);
}
buffer.append("->");
}
if (myHelper.getStatements().length > 1) {
if (helper.getStatements().length > 1) {
buffer.append('\n');
}
ExtractUtil.generateBody(myHelper, false, buffer);
ExtractUtil.generateBody(helper, false, buffer);
buffer.append('}');
return GroovyPsiElementFactory.getInstance(myHelper.getProject()).createClosureFromText(buffer.toString(), myHelper.getToReplaceIn());
return GroovyPsiElementFactory.getInstance(helper.getProject()).createClosureFromText(buffer.toString(), helper.getToReplaceIn());
}
}
@@ -98,10 +98,9 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting
return expressions;
}
private static boolean expressionIsIncorrect(GrExpression expression) {
public static boolean expressionIsIncorrect(GrExpression expression) {
if (expression instanceof GrParenthesizedExpression) return true;
if (expression instanceof GrSuperReferenceExpression) return true;
if (expression.getType() == PsiType.VOID) return true;
if (expression instanceof GrAssignmentExpression) return true;
if (expression instanceof GrReferenceExpression && expression.getParent() instanceof GrCall) {
final GroovyResolveResult resolveResult = ((GrReferenceExpression)expression).advancedResolve();
@@ -331,11 +330,7 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting
if (type != null) type = TypeConversionUtil.erasure(type);
if (PsiType.VOID.equals(type)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.expression.has.void.type"));
}
if (expressionIsIncorrect(selected)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.block.should.represent.an.expression"));
return null;
}
return selected;
@@ -1,156 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterDialog">
<grid id="cbd77" binding="myContentPane" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="48" y="54" width="595" height="340"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="ad8bd" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="a9bd6" class="javax.swing.JLabel" binding="myTypeLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Parameter of &amp;type:"/>
</properties>
</component>
<component id="eeb4" class="org.jetbrains.plugins.groovy.refactoring.ui.GrTypeComboBox" binding="myTypeComboBox" custom-create="true" default-binding="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="7aff5" class="javax.swing.JLabel" binding="myNameLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="&amp;Name:"/>
</properties>
</component>
<component id="d934b" class="com.intellij.refactoring.ui.NameSuggestionsField" binding="myNameSuggestionsField" custom-create="true" default-binding="true">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<grid id="a8cf9" binding="myCheckBoxContainer" layout-manager="GridBagLayout">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="62ec5" class="javax.swing.JCheckBox" binding="myDeclareFinalCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<gridbag weightx="1.0" weighty="1.0"/>
</constraints>
<properties>
<text value="Declare &amp;final"/>
</properties>
</component>
<component id="33f30" class="javax.swing.JCheckBox" binding="myDelegateViaOverloadingMethodCheckBox" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<gridbag weightx="1.0" weighty="1.0"/>
</constraints>
<properties>
<text value="De&amp;legate via overloading method"/>
</properties>
</component>
<component id="e1dc5" class="javax.swing.JCheckBox" binding="myReplaceAllOccurrencesCheckBox" default-binding="true">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<gridbag weightx="1.0" weighty="1.0"/>
</constraints>
<properties>
<text value="Replace &amp;all occurrences"/>
</properties>
</component>
<component id="a952e" class="javax.swing.JCheckBox" binding="myRemoveLocalVariableCheckBox" default-binding="true">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<gridbag weightx="1.0" weighty="1.0"/>
</constraints>
<properties>
<text value="Remove local &amp;variable"/>
</properties>
</component>
<component id="d4b2" class="javax.swing.JCheckBox" binding="myChangeVarUsages">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<gridbag weightx="0.0" weighty="0.0"/>
</constraints>
<properties>
<text value="&amp;Change usages of closure"/>
</properties>
</component>
</children>
</grid>
<grid id="537d" binding="myGetterPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
</clientProperties>
<border type="none" title="Peplace fields used in expressions with their getters"/>
<children>
<component id="5cbea" class="javax.swing.JRadioButton" binding="myDoNotReplaceRadioButton" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="Do n&amp;ot replace"/>
</properties>
</component>
<component id="621e5" class="javax.swing.JRadioButton" binding="myReplaceFieldsInaccessibleInRadioButton" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="false"/>
<text value="Replace fields &amp;inaccessible in usage context"/>
</properties>
</component>
<component id="9d2ba" class="javax.swing.JRadioButton" binding="myReplaceAllFieldsRadioButton" default-binding="true">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="&amp;Replace all fields"/>
</properties>
</component>
</children>
</grid>
<vspacer id="14a96">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
<buttonGroups>
<group name="replaceFieldsGroup">
<member id="5cbea"/>
<member id="621e5"/>
<member id="9d2ba"/>
</group>
</buttonGroups>
</form>
@@ -15,23 +15,31 @@
*/
package org.jetbrains.plugins.groovy.refactoring.introduce.parameter;
import com.intellij.psi.*;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiType;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.ui.NameSuggestionsField;
import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.util.ArrayUtil;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.util.ui.GridBag;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntArrayList;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
@@ -40,38 +48,42 @@ import org.jetbrains.plugins.groovy.refactoring.GrRefactoringError;
import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.HelpID;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel;
import org.jetbrains.plugins.groovy.refactoring.extract.closure.ExtractClosureFromMethodProcessor;
import org.jetbrains.plugins.groovy.refactoring.extract.closure.ExtractClosureHelperImpl;
import org.jetbrains.plugins.groovy.refactoring.extract.closure.ExtractClosureProcessorBase;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContextImpl;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceDialog;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase;
import org.jetbrains.plugins.groovy.refactoring.introduce.field.GroovyFieldValidator;
import org.jetbrains.plugins.groovy.refactoring.ui.GrMethodSignatureComponent;
import org.jetbrains.plugins.groovy.refactoring.ui.GrTypeComboBox;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import static com.intellij.refactoring.IntroduceParameterRefactoring.*;
public class GrIntroduceParameterDialog extends RefactoringDialog implements GrIntroduceDialog<GrIntroduceParameterSettings> {
private JPanel myContentPane;
private GrTypeComboBox myTypeComboBox;
private NameSuggestionsField myNameSuggestionsField;
private JCheckBox myDeclareFinalCheckBox;
private JCheckBox myDelegateViaOverloadingMethodCheckBox;
private JPanel myCheckBoxContainer;
private JCheckBox myReplaceAllOccurrencesCheckBox;
private JRadioButton myDoNotReplaceRadioButton;
private JRadioButton myReplaceFieldsInaccessibleInRadioButton;
private JRadioButton myReplaceAllFieldsRadioButton;
private JCheckBox myRemoveLocalVariableCheckBox;
private JBRadioButton myDoNotReplaceRadioButton;
private JBRadioButton myReplaceFieldsInaccessibleInRadioButton;
private JBRadioButton myReplaceAllFieldsRadioButton;
private JPanel myGetterPanel;
private JLabel myTypeLabel;
private JLabel myNameLabel;
private JCheckBox myChangeVarUsages;
private IntroduceParameterInfo myInfo;
TObjectIntHashMap<JCheckBox> toRemoveCBs;
private TObjectIntHashMap<JCheckBox> toRemoveCBs;
private GrMethodSignatureComponent mySignature;
private ParameterTablePanel myTable;
private JPanel mySignaturePanel;
public GrIntroduceParameterDialog(IntroduceParameterInfo info) {
super(info.getProject(), true);
@@ -85,31 +97,271 @@ public class GrIntroduceParameterDialog extends RefactoringDialog implements GrI
cb.setSelected(true);
}
JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
init();
}
if (myInfo.getStatements().length == 1 && GrIntroduceHandlerBase.findVariable(myInfo.getStatements()[0]) == null) {
myRemoveLocalVariableCheckBox.setSelected(false);
myRemoveLocalVariableCheckBox.setVisible(false);
}
else {
myRemoveLocalVariableCheckBox.setSelected(settings.INTRODUCE_PARAMETER_DELETE_LOCAL_VARIABLE);
}
@Override
protected void init() {
super.init();
JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
initReplaceFieldsWithGetters(settings);
myDeclareFinalCheckBox.setSelected(hasFinalModifier());
myChangeVarUsages.setVisible(info.getToReplaceIn() instanceof GrClosableBlock && info.getToSearchFor() instanceof GrVariable);
myChangeVarUsages.setSelected(true);
myDelegateViaOverloadingMethodCheckBox.setVisible(info.getToSearchFor() != null);
myDelegateViaOverloadingMethodCheckBox.setVisible(myInfo.getToSearchFor() != null);
setTitle(RefactoringBundle.message("introduce.parameter.title"));
init();
myTable.init(myInfo);
final GrParameter[] parameters = myInfo.getToReplaceIn().getParameters();
toRemoveCBs.forEachEntry(new TObjectIntProcedure<JCheckBox>() {
@Override
public boolean execute(JCheckBox checkbox, int index) {
checkbox.setSelected(true);
final GrParameter param = parameters[index];
final ParameterInfo pinfo = findParamByOldName(param.getName());
if (pinfo != null) {
pinfo.setPassAsParameter(false);
}
return true;
}
});
updateSignature();
if (findExpr() != null || findVar() != null) {
mySignaturePanel.setVisible(false);
}
myTypeComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
mySignaturePanel.setVisible(myTypeComboBox.isClosureSelected());
pack();
}
});
pack();
}
@Override
protected JComponent createCenterPanel() {
JPanel north = new JPanel();
north.setLayout(new BoxLayout(north, BoxLayout.Y_AXIS));
north.add(createNamePanel());
createCheckBoxes(north);
myGetterPanel = createFieldPanel();
north.add(myGetterPanel);
final JPanel root = new JPanel(new BorderLayout());
mySignaturePanel = createSignaturePanel();
root.add(mySignaturePanel, BorderLayout.CENTER);
root.add(north, BorderLayout.NORTH);
return root;
}
private JPanel createSignaturePanel() {
mySignature = new GrMethodSignatureComponent("", myProject);
myTable = new ParameterTablePanel() {
@Override
protected void updateSignature() {
GrIntroduceParameterDialog.this.updateSignature();
}
@Override
protected void doEnterAction() {
clickDefaultButton();
}
@Override
protected void doCancelAction() {
GrIntroduceParameterDialog.this.doCancelAction();
}
};
mySignature.setBorder(IdeBorderFactory.createTitledBorder(GroovyRefactoringBundle.message("signature.preview.border.title"), false, false, true));
Splitter splitter = new Splitter(true);
splitter.setFirstComponent(myTable);
splitter.setSecondComponent(mySignature);
mySignature.setPreferredSize(new Dimension(500, 100));
mySignature.setSize(new Dimension(500, 100));
splitter.setShowDividerIcon(false);
return splitter;
}
private void updateSignature() {
StringBuilder b = new StringBuilder();
b.append("{ ");
String[] params = ExtractUtil.getParameterString(myInfo, false);
for (int i = 0; i < params.length; i++) {
if (i > 0) {
b.append(" ");
}
b.append(params[i]);
b.append('\n');
}
b.append(" ->\n}");
mySignature.setSignature(b.toString());
}
@Override
protected ValidationInfo doValidate() {
final String text = myNameSuggestionsField.getEnteredName();
if (!StringUtil.isJavaIdentifier(text)) {
return new ValidationInfo(GroovyRefactoringBundle.message("name.is.wrong", text), myNameSuggestionsField);
}
final Ref<ValidationInfo> info = new Ref<ValidationInfo>();
toRemoveCBs.forEachEntry(new TObjectIntProcedure<JCheckBox>() {
@Override
public boolean execute(JCheckBox checkbox, int index) {
if (!checkbox.isSelected()) return true;
final GrParameter param = myInfo.getToReplaceIn().getParameters()[index];
final ParameterInfo pinfo = findParamByOldName(param.getName());
if (pinfo == null || !pinfo.passAsParameter()) return true;
final String message = GroovyRefactoringBundle.message("you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method",
pinfo.getName(), param.getName());
info.set(new ValidationInfo(message));
return false;
}
});
return info.get();
}
@Nullable
private ParameterInfo findParamByOldName(String name) {
for (ParameterInfo info : myInfo.getParameterInfos()) {
if (name.equals(info.getOldName())) return info;
}
return null;
}
private JPanel createFieldPanel() {
myDoNotReplaceRadioButton = new JBRadioButton(UIUtil.replaceMnemonicAmpersand("Do n&ot replace"));
myReplaceFieldsInaccessibleInRadioButton = new JBRadioButton(UIUtil.replaceMnemonicAmpersand("Replace fields &inaccessible in usage context"));
myReplaceAllFieldsRadioButton = new JBRadioButton(UIUtil.replaceMnemonicAmpersand("&Replace all fields"));
myDoNotReplaceRadioButton.setFocusable(false);
myReplaceFieldsInaccessibleInRadioButton.setFocusable(false);
myReplaceAllFieldsRadioButton.setFocusable(false);
final ButtonGroup group = new ButtonGroup();
group.add(myDoNotReplaceRadioButton);
group.add(myReplaceFieldsInaccessibleInRadioButton);
group.add(myReplaceAllFieldsRadioButton);
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(myDoNotReplaceRadioButton);
panel.add(myReplaceFieldsInaccessibleInRadioButton);
panel.add(myReplaceAllFieldsRadioButton);
panel.setBorder(IdeBorderFactory.createTitledBorder(UIUtil.replaceMnemonicAmpersand("Replace fields used in expression with their getters"), false, false, true));
return panel;
}
private JPanel createNamePanel() {
final GridBag c = new GridBag().setDefaultAnchor(GridBagConstraints.WEST).setDefaultInsets(1, 1, 1, 1);
final JPanel namePanel = new JPanel(new GridBagLayout());
final JLabel nameLabel = new JLabel(UIUtil.replaceMnemonicAmpersand("&Name:"));
c.nextLine().next().weightx(0).fillCellNone();
namePanel.add(nameLabel, c);
myNameSuggestionsField = createNameField(findVar(), findExpr());
c.next().weightx(1).fillCellHorizontally();
namePanel.add(myNameSuggestionsField, c);
nameLabel.setLabelFor(myNameSuggestionsField);
final JLabel typeLabel = new JLabel(UIUtil.replaceMnemonicAmpersand("&Type:"));
c.nextLine().next().weightx(0).fillCellNone();
namePanel.add(typeLabel, c);
myTypeComboBox = createTypeComboBox(findVar(), findExpr());
c.next().weightx(1).fillCellHorizontally();
namePanel.add(myTypeComboBox, c);
typeLabel.setLabelFor(myTypeComboBox);
return namePanel;
}
private void createCheckBoxes(JPanel panel) {
myDeclareFinalCheckBox = new JCheckBox(UIUtil.replaceMnemonicAmpersand("Declare &final"));
myDeclareFinalCheckBox.setFocusable(false);
panel.add(myDeclareFinalCheckBox);
myDelegateViaOverloadingMethodCheckBox = new JCheckBox(UIUtil.replaceMnemonicAmpersand("De&legate via overloading method"));
myDelegateViaOverloadingMethodCheckBox.setFocusable(false);
panel.add(myDelegateViaOverloadingMethodCheckBox);
for (Object o : toRemoveCBs.keys()) {
final JCheckBox cb = (JCheckBox)o;
cb.setFocusable(false);
panel.add(cb);
}
}
private GrTypeComboBox createTypeComboBox(GrVariable var, GrExpression expr) {
GrTypeComboBox box;
if (var != null) {
box = GrTypeComboBox.createTypeComboBoxWithDefType(var.getDeclaredType());
}
else if (expr != null) {
box = GrTypeComboBox.createTypeComboBoxFromExpression(expr);
}
else {
box = GrTypeComboBox.createEmptyTypeComboBox();
}
final ExtractClosureHelperImpl mockHelper = new ExtractClosureHelperImpl(myInfo, "__test___n_", false, new TIntArrayList(), false, 0);
final PsiType returnType = ExtractClosureProcessorBase.generateClosure(mockHelper).getReturnType();
box.addClosureTypesFrom(returnType, mockHelper.getContext());
if (expr == null && var == null) {
box.setSelectedIndex(box.getItemCount() - 1);
}
return box;
}
private NameSuggestionsField createNameField(GrVariable var, GrExpression expr) {
String[] possibleNames;
if (expr != null) {
final GrIntroduceContext
introduceContext = new GrIntroduceContextImpl(myProject, null, expr, var, PsiElement.EMPTY_ARRAY, myInfo.getToReplaceIn());
final GroovyFieldValidator validator = new GroovyFieldValidator(introduceContext);
possibleNames = GroovyNameSuggestionUtil.suggestVariableNames(expr, validator, true);
}
else if (var != null) {
final GrIntroduceContext introduceContext =
new GrIntroduceContextImpl(myProject, null, expr, var, PsiElement.EMPTY_ARRAY, myInfo.getToReplaceIn());
final GroovyFieldValidator validator = new GroovyFieldValidator(introduceContext);
possibleNames = GroovyNameSuggestionUtil.suggestVariableNameByType(var.getType(), validator);
}
else {
possibleNames = new String[]{"closure"};
}
if (var != null) {
String[] arr = new String[possibleNames.length + 1];
arr[0] = var.getName();
System.arraycopy(possibleNames, 0, arr, 1, possibleNames.length);
possibleNames = arr;
}
return new NameSuggestionsField(possibleNames, myProject, GroovyFileType.GROOVY_FILE_TYPE);
}
private void initReplaceFieldsWithGetters(JavaRefactoringSettings settings) {
final PsiField[] usedFields = GroovyIntroduceParameterUtil.findUsedFieldsWithGetters(myInfo.getStatements(), getContainingClass());
myGetterPanel.setVisible(usedFields.length > 0);
switch (settings.INTRODUCE_PARAMETER_REPLACE_FIELDS_WITH_GETTERS) {
@@ -182,27 +434,11 @@ public class GrIntroduceParameterDialog extends RefactoringDialog implements GrI
private void saveSettings() {
final JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
settings.INTRODUCE_PARAMETER_CREATE_FINALS = myDeclareFinalCheckBox.isSelected();
if (myRemoveLocalVariableCheckBox.isVisible()) {
settings.INTRODUCE_PARAMETER_DELETE_LOCAL_VARIABLE = myRemoveLocalVariableCheckBox.isSelected();
}
if (myGetterPanel.isVisible()) {
settings.INTRODUCE_PARAMETER_REPLACE_FIELDS_WITH_GETTERS = getReplaceFieldsWithGetter();
}
}
@Override
protected JComponent createCenterPanel() {
final GridBag c = new GridBag().setDefaultAnchor(GridBagConstraints.WEST).setLine(4);
for (Object o : toRemoveCBs.keys()) {
c.nextLine();
myCheckBoxContainer.add(((JCheckBox)o), c);
}
myNameLabel.setLabelFor(myNameSuggestionsField);
myTypeLabel.setLabelFor(myTypeComboBox);
return myContentPane;
}
@Override
public GrIntroduceParameterSettings getSettings() {
return null;
@@ -235,42 +471,6 @@ public class GrIntroduceParameterDialog extends RefactoringDialog implements GrI
return list;
}
private void createUIComponents() {
final GrVariable var = findVar();
final GrExpression expr = findExpr();
if (var != null) {
myTypeComboBox = GrTypeComboBox.createTypeComboBoxWithDefType(var.getDeclaredType());
}
else if (expr != null) {
myTypeComboBox = GrTypeComboBox.createTypeComboBoxFromExpression(expr);
}
else {
myTypeComboBox = GrTypeComboBox.createEmptyTypeComboBox();
}
myTypeComboBox.addType(JavaPsiFacade.getElementFactory(myProject).createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, myInfo.getContext()));
String[] possibleNames;
final GrIntroduceContext introduceContext = new GrIntroduceContextImpl(myProject, null, expr, var, PsiElement.EMPTY_ARRAY, myInfo.getToReplaceIn());
final GroovyFieldValidator validator = new GroovyFieldValidator(introduceContext);
if (expr != null) {
possibleNames = GroovyNameSuggestionUtil.suggestVariableNames(expr, validator, true);
}
else if (var != null) {
possibleNames = GroovyNameSuggestionUtil.suggestVariableNameByType(var.getType(), validator);
}
else {
possibleNames = ArrayUtil.EMPTY_STRING_ARRAY;
}
if (var != null) {
String[] arr = new String[possibleNames.length + 1];
arr[0] = var.getName();
System.arraycopy(possibleNames, 0, arr, 1, possibleNames.length);
possibleNames = arr;
}
myNameSuggestionsField = new NameSuggestionsField(possibleNames, myProject, GroovyFileType.GROOVY_FILE_TYPE);
}
@Nullable
private GrVariable findVar() {
@@ -25,6 +25,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.*;
@@ -99,8 +100,10 @@ public class GrTypeComboBox extends ComboBox {
}
}
public void addType(PsiType type) {
addItem(new PsiTypeItem(type));
public void addClosureTypesFrom(PsiType type, PsiElement context) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
final PsiType cl = factory.createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE + '<' + type.getCanonicalText() + '>', context);
addItem(new PsiTypeItem(cl, true));
}
@Nullable
@@ -110,6 +113,10 @@ public class GrTypeComboBox extends ComboBox {
return ((PsiTypeItem)selected).getType();
}
public boolean isClosureSelected() {
return ((PsiTypeItem)getSelectedItem()).isClosure();
}
private static Map<String, PsiType> getCompatibleTypeNames(@NotNull PsiType type,
@Nullable PsiType min,
@@ -165,8 +172,15 @@ public class GrTypeComboBox extends ComboBox {
@Nullable
private final PsiType myType;
private final boolean isClosure;
private PsiTypeItem(final PsiType type) {
this(type, false);
}
private PsiTypeItem(final PsiType type, boolean closure) {
myType = type;
isClosure = closure;
}
@Nullable
@@ -200,5 +214,9 @@ public class GrTypeComboBox extends ComboBox {
public String toString() {
return myType == null ? "def" : myType.getPresentableText();
}
public boolean isClosure() {
return isClosure;
}
}
}