extract method: make static and pass fields as parameters if possible (IDEA-112230)

This commit is contained in:
Anna Kozlova
2014-12-03 14:27:46 +01:00
parent e540615904
commit 460771192f
8 changed files with 125 additions and 12 deletions
@@ -33,6 +33,7 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.util.VariableData;
import com.intellij.refactoring.util.duplicates.DuplicatesFinder;
import com.intellij.util.ArrayUtil;
import com.intellij.util.text.UniqueNameGenerator;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -47,6 +48,9 @@ public class InputVariables {
private ParametersFolder myFolding;
private boolean myFoldingAvailable;
private Set<PsiField> myUsedInstanceFields = null;
private boolean myPassFields = false;
public InputVariables(final List<? extends PsiVariable> inputVariables,
Project project,
LocalSearchScope scope,
@@ -74,16 +78,25 @@ public class InputVariables {
return myFolding.isFoldable();
}
public void setUsedInstanceFields(Set<PsiField> usedInstanceFields) {
myUsedInstanceFields = usedInstanceFields;
}
public void setPassFields(boolean passFields) {
if (myUsedInstanceFields == null || myUsedInstanceFields.isEmpty()) {
return;
}
myPassFields = passFields;
myInputVariables.clear();
myInputVariables.addAll(wrapInputVariables(myInitialParameters));
}
public ArrayList<VariableData> wrapInputVariables(final List<? extends PsiVariable> inputVariables) {
UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
final ArrayList<VariableData> inputData = new ArrayList<VariableData>(inputVariables.size());
for (PsiVariable var : inputVariables) {
String name = var.getName();
if (!(var instanceof PsiParameter)) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(myProject);
VariableKind kind = codeStyleManager.getVariableKind(var);
name = codeStyleManager.variableNameToPropertyName(name, kind);
name = codeStyleManager.propertyNameToVariableName(name, VariableKind.PARAMETER);
}
String name = nameGenerator.generateUniqueName(getParameterName(var));
PsiType type = var.getType();
if (type instanceof PsiEllipsisType) {
type = ((PsiEllipsisType)type).toArrayType();
@@ -138,9 +151,28 @@ public class InputVariables {
}
if (myPassFields && myUsedInstanceFields != null) {
for (PsiField var : myUsedInstanceFields) {
final VariableData data = new VariableData(var, var.getType());
data.name = nameGenerator.generateUniqueName(getParameterName(var));
data.passAsParameter = true;
inputData.add(data);
}
}
return inputData;
}
private String getParameterName(PsiVariable var) {
String name = var.getName();
if (!(var instanceof PsiParameter)) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(myProject);
VariableKind kind = codeStyleManager.getVariableKind(var);
name = codeStyleManager.variableNameToPropertyName(name, kind);
name = codeStyleManager.propertyNameToVariableName(name, VariableKind.PARAMETER);
}
return name;
}
@Nullable
private PsiType checkTopLevelInstanceOf(final PsiType currentType) {
final PsiElement[] scope = myScope.getScope();
@@ -310,4 +342,8 @@ public class InputVariables {
public boolean isFoldingSelectedByDefault() {
return myFolding.isFoldingSelectedByDefault();
}
public boolean hasInstanceFields() {
return myUsedInstanceFields != null && !myUsedInstanceFields.isEmpty();
}
}
@@ -280,8 +280,17 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
if (myStaticFlag || myCanBeStatic) {
myMakeStatic.setEnabled(!myStaticFlag);
myMakeStatic.setSelected(myStaticFlag);
if (myVariableData.hasInstanceFields()) {
myMakeStatic.setText(RefactoringBundle.message("declare.static.pass.fields.checkbox"));
}
myMakeStatic.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (myVariableData.hasInstanceFields()) {
myVariableData.setPassFields(myMakeStatic.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
updateVarargsEnabled();
createParametersPanel();
}
updateSignature();
}
});
@@ -638,7 +638,14 @@ public class ExtractMethodProcessor implements MatchProvider {
}
@TestOnly
public void testPrepare(PsiType returnType) {
public void testPrepare(PsiType returnType, boolean makeStatic) throws PrepareFailedException{
if (makeStatic) {
if (!isCanBeStatic()) {
throw new PrepareFailedException("Failed to make static", myElements[0]);
}
myInputVariables.setPassFields(true);
myStatic = true;
}
testPrepare();
if (returnType != null) {
myReturnType = returnType;
@@ -1464,8 +1471,21 @@ public class ExtractMethodProcessor implements MatchProvider {
private boolean applyChosenClassAndExtract(List<PsiVariable> inputVariables, @Nullable Pass<ExtractMethodProcessor> extractPass)
throws PrepareFailedException {
myStatic = shouldBeStatic();
final Set<PsiField> fields = new LinkedHashSet<PsiField>();
if (!PsiUtil.isLocalOrAnonymousClass(myTargetClass) && (myTargetClass.getContainingClass() == null || myTargetClass.hasModifierProperty(PsiModifier.STATIC))) {
ElementNeedsThis needsThis = new ElementNeedsThis(myTargetClass);
ElementNeedsThis needsThis = new ElementNeedsThis(myTargetClass) {
@Override
protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) {
if (classMember instanceof PsiField && !classMember.hasModifierProperty(PsiModifier.STATIC)) {
final PsiExpression expression = PsiTreeUtil.getParentOfType(classMemberReference, PsiExpression.class, false);
if (expression == null || !PsiUtil.isAccessedForWriting(expression)) {
fields.add((PsiField)classMember);
return;
}
}
super.visitClassMemberReferenceElement(classMember, classMemberReference);
}
};
for (int i = 0; i < myElements.length && !needsThis.usesMembers(); i++) {
PsiElement element = myElements[i];
element.accept(needsThis);
@@ -1477,6 +1497,7 @@ public class ExtractMethodProcessor implements MatchProvider {
}
myInputVariables = new InputVariables(inputVariables, myProject, new LocalSearchScope(myElements), true);
myInputVariables.setUsedInstanceFields(fields);
if (!checkExitPoints()){
return false;
@@ -0,0 +1,6 @@
class X {
private int myI;
void foo() {
<selection>int i = myI++;</selection>
}
}
@@ -0,0 +1,6 @@
class X {
private int myI;
void foo() {
<selection>int i = myI;</selection>
}
}
@@ -0,0 +1,10 @@
class X {
private int myI;
void foo() {
newMethod(myI);
}
private static void newMethod(int myI) {
int i = myI;
}
}
@@ -625,6 +625,19 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doTestReturnTypeChanged(PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())));
}
public void testPassFieldAsParameterAndMakeStatic() throws Exception {
doTestPassFieldsAsParams();
}
public void testCantPassFieldAsParameter() throws Exception {
try {
doTestPassFieldsAsParams();
fail("Field was modified inside. Make static should be disabled");
}
catch (PrepareFailedException ignore) {
}
}
private void doTestDisabledParam() throws PrepareFailedException {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.ELSE_ON_NEW_LINE = true;
@@ -640,7 +653,17 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
settings.ELSE_ON_NEW_LINE = true;
settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
configureByFile(BASE_PATH + getTestName(false) + ".java");
boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, type);
boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, type, false);
assertTrue(success);
checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
}
private void doTestPassFieldsAsParams() throws PrepareFailedException {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.ELSE_ON_NEW_LINE = true;
settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
configureByFile(BASE_PATH + getTestName(false) + ".java");
boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, true);
assertTrue(success);
checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
}
@@ -700,7 +723,7 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
final boolean extractChainedConstructor,
int... disabledParams)
throws PrepareFailedException, IncorrectOperationException {
return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, extractChainedConstructor, null, disabledParams);
return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, extractChainedConstructor, null, false, disabledParams);
}
public static boolean performExtractMethod(boolean doRefactor,
@@ -710,6 +733,7 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
Project project,
final boolean extractChainedConstructor,
PsiType returnType,
boolean makeStatic,
int... disabledParams)
throws PrepareFailedException, IncorrectOperationException {
int startOffset = editor.getSelectionModel().getSelectionStart();
@@ -741,7 +765,7 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
}
if (doRefactor) {
processor.testPrepare(returnType);
processor.testPrepare(returnType, makeStatic);
processor.testNullness();
if (disabledParams != null) {
for (int param : disabledParams) {
@@ -397,6 +397,7 @@ introduce.constant.field.of.type=Constant (static final field) of &type:
extract.subclass.command=Extract Subclass
extract.superclass.elements.header=Extract superclass with members to directory
declare.static.checkbox=Declare &static
declare.static.pass.fields.checkbox=Declare &static (pass fields as params)
declare.varargs.checkbox=Declare v&arargs
declare.folded.parameters=&Fold parameters
extract.method.method.panel.border=Method