extract class: field initializers fixed (IDEA-52622)

This commit is contained in:
anna
2010-03-05 14:07:35 +03:00
parent 1522c2378e
commit 9e086ac5f3
22 changed files with 168 additions and 149 deletions
@@ -38,6 +38,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.RefactorJBundle;
import com.intellij.refactoring.extractclass.usageInfo.*;
import com.intellij.refactoring.move.MoveInstanceMembersUtil;
import com.intellij.refactoring.psi.MethodInheritanceUtils;
import com.intellij.refactoring.psi.TypeParametersVisitor;
import com.intellij.refactoring.util.FixableUsageInfo;
@@ -236,6 +237,54 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
final PsiField member = psiClass.findFieldByName(field.getName(), false);
if (member != null) {
members.add(member);
final PsiExpression initializer = member.getInitializer();
if (initializer != null) {
final boolean[] moveInitializerToConstructor = new boolean[1];
initializer.accept(new JavaRecursiveElementWalkingVisitor(){
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolved = expression.resolve();
if (resolved instanceof PsiField && !members.contains(resolved)) {
moveInitializerToConstructor[0] = true;
}
}
});
if (moveInitializerToConstructor[0]) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myProject);
PsiMethod constructor;
final PsiMethod[] constructors = psiClass.getConstructors();
if (constructors.length == 0) {
constructor = elementFactory.createConstructor();
constructor.setName(psiClass.getName());
constructor = (PsiMethod)psiClass.add(constructor);
} else {
constructor = constructors[0];
}
MoveInstanceMembersUtil.moveInitializerToConstructor(elementFactory, constructor, member);
}
}
}
}
if (myGenerateAccessors) {
final NecessaryAccessorsVisitor visitor = new NecessaryAccessorsVisitor();
for (PsiField field : fields) {
field.accept(visitor);
}
for (PsiMethod method : methods) {
method.accept(visitor);
}
for (PsiClass innerClass : innerClasses) {
innerClass.accept(visitor);
}
for (PsiField field : visitor.getFieldsNeedingGetter()) {
sourceClass.add(PropertyUtil.generateGetterPrototype(field));
}
for (PsiField field : visitor.getFieldsNeedingSetter()) {
sourceClass.add(PropertyUtil.generateSetterPrototype(field));
}
}
super.performRefactoring(usageInfos);
@@ -673,19 +722,25 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
super.visitReferenceExpression(expression);
if (isProhibitedReference(expression)) {
final PsiField field = getReferencedField(expression);
if (!hasGetter(field)) {
if (!hasGetter(field) && !isStaticFinal(field)) {
fieldsNeedingGetter.add(field);
}
}
}
private boolean isStaticFinal(PsiField field) {
final PsiModifierList modifierList = field.getModifierList();
logger.assertTrue(modifierList != null);
return modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL);
}
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
super.visitAssignmentExpression(expression);
final PsiExpression lhs = expression.getLExpression();
if (isProhibitedReference(lhs)) {
final PsiField field = getReferencedField(lhs);
if (!hasGetter(field)) {
if (!hasGetter(field) && !isStaticFinal(field)) {
fieldsNeedingSetter.add(field);
}
}
@@ -708,7 +763,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
}
if (isProhibitedReference(operand)) {
final PsiField field = getReferencedField(operand);
if (!hasSetter(field)) {
if (!hasSetter(field) && !isStaticFinal(field)) {
fieldsNeedingSetter.add(field);
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
@@ -153,7 +152,7 @@ class ExtractedClassBuilder {
out.append(' ' + backPointerName + ";");
}
outputFieldsAndInitializers(out);
if (hasNonStatic() || requiresBackPointer) {
if (needConstructor() || requiresBackPointer) {
outputConstructor(out);
}
outputMethods(out);
@@ -206,7 +205,7 @@ class ExtractedClassBuilder {
return false;
}
private boolean hasNonStatic() {
private boolean needConstructor() {
for ( PsiField field : fields) {
if (!field.hasModifierProperty(PsiModifier.STATIC)) {
return true;
@@ -303,7 +302,6 @@ class ExtractedClassBuilder {
private void outputConstructor(@NonNls StringBuffer out) {
out.append("\tpublic " + className + '(');
boolean isFirst = true;
if (requiresBackPointer) {
final String parameterName = myJavaCodeStyleManager.propertyNameToVariableName(backPointerName, VariableKind.PARAMETER);
out.append(originalClassName);
@@ -320,28 +318,6 @@ class ExtractedClassBuilder {
out.append('>');
}
out.append(' ' + parameterName);
isFirst = false;
}
for (final PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
if (!field.hasInitializer()) {
continue;
}
final PsiExpression initializer = field.getInitializer();
if (PsiUtil.isConstantExpression(initializer)) {
continue;
}
if (!isFirst) {
out.append(", ");
}
isFirst = false;
final PsiType type = field.getType();
final String typeText = type.getCanonicalText();
final String name = calculateStrippedName(field);
final String parameterName = myJavaCodeStyleManager.propertyNameToVariableName(name, VariableKind.PARAMETER);
out.append(typeText + ' ' + parameterName);
}
out.append(")");
@@ -356,84 +332,11 @@ class ExtractedClassBuilder {
}
}
for (final PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
if (!field.hasInitializer()) {
continue;
}
final PsiExpression initializer = field.getInitializer();
if (PsiUtil.isConstantExpression(initializer)) {
continue;
}
final String name = calculateStrippedName(field);
final String parameterName = myJavaCodeStyleManager.propertyNameToVariableName(name, VariableKind.PARAMETER);
final String fieldName = field.getName();
if (fieldName.equals(parameterName)) {
out.append("\t\tthis." + fieldName + " = " + parameterName + ";");
}
else {
out.append("\t\t" + fieldName + " = " + parameterName + ";");
}
}
out.append("\t}");
}
private void outputField(PsiField field, @NonNls StringBuffer out) {
final PsiDocComment docComment = getJavadocForVariable(field);
if (docComment != null) {
out.append(docComment.getText());
}
final PsiType type = field.getType();
final String typeText = type.getCanonicalText();
final String name = field.getName();
@NonNls String modifierString;
if (field.hasModifierProperty(PsiModifier.PUBLIC) && field.hasModifierProperty(PsiModifier.STATIC)) {
modifierString = "public ";
}
else {
modifierString = "private ";
}
if (field.hasModifierProperty(PsiModifier.STATIC)) {
modifierString += "static ";
}
if (field.hasModifierProperty(PsiModifier.FINAL) && (myFieldsNeedingSetters == null || !myFieldsNeedingSetters.contains(field))) {
modifierString += "final ";
}
if (field.hasModifierProperty(PsiModifier.TRANSIENT)) {
modifierString += "transient ";
}
final PsiModifierList modifierList = field.getModifierList();
final PsiAnnotation[] annotations = modifierList.getAnnotations();
for (PsiAnnotation annotation : annotations) {
final String annotationText = annotation.getText();
out.append(annotationText);
}
out.append('\t');
out.append(modifierString);
out.append(typeText);
out.append(' ');
out.append(name);
if (field.hasInitializer()) {
final PsiExpression initializer = field.getInitializer();
if (PsiUtil.isConstantExpression(initializer)) {
out.append('=');
out.append(initializer.getText());
}
}
out.append(";");
}
private static PsiDocComment getJavadocForVariable(PsiVariable variable) {
final PsiElement[] children = variable.getChildren();
for (PsiElement child : children) {
if (child instanceof PsiDocComment) {
return (PsiDocComment)child;
}
}
return null;
field.accept(new Mutator(out));
}
public void setRequiresBackPointer(boolean requiresBackPointer) {
@@ -454,6 +357,16 @@ class ExtractedClassBuilder {
myFieldsNeedingSetters = fieldsNeedingSetters;
}
private boolean fieldIsExtracted(PsiField field) {
for (PsiField psiField : fields) {
if (psiField.equals(field)) {
return true;
}
}
final PsiClass containingClass = field.getContainingClass();
return innerClasses.contains(containingClass);
}
private class Mutator extends JavaElementVisitor {
@NonNls
private final StringBuffer out;
@@ -618,15 +531,7 @@ class ExtractedClassBuilder {
out.append(backPointerName);
}
private boolean fieldIsExtracted(PsiField field) {
for (PsiField psiField : fields) {
if (psiField.equals(field)) {
return true;
}
}
final PsiClass containingClass = field.getContainingClass();
return innerClasses.contains(containingClass);
}
public void visitMethodCallExpression(PsiMethodCallExpression call) {
final PsiReferenceExpression expression = call.getMethodExpression();
@@ -27,6 +27,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.move.MoveInstanceMembersUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper;
@@ -116,7 +117,7 @@ public class MakeClassStaticProcessor extends MakeMethodOrClassStaticProcessor<P
if (referencedElement instanceof PsiField && mySettings.getNameForField((PsiField)referencedElement) != null) {
final PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class);
if (field != null) {
moveInitializerToConstructor(factory, constructor, field);
MoveInstanceMembersUtil.moveInitializerToConstructor(factory, constructor, field);
}
}
}
@@ -133,34 +134,6 @@ public class MakeClassStaticProcessor extends MakeMethodOrClassStaticProcessor<P
modifierList.setModifierProperty(PsiModifier.FINAL, false);
}
private static void moveInitializerToConstructor(PsiElementFactory factory, PsiMethod constructor, PsiField field) {
final PsiExpression initializer = field.getInitializer();
PsiExpression initializerCopy = (PsiExpression)initializer.copy();
final PsiCodeBlock body = constructor.getBody();
if (body != null) {
try {
String fieldName = field.getName();
final PsiReferenceExpression refExpr = (PsiReferenceExpression)factory.createExpressionFromText(fieldName, body);
if (refExpr.resolve() != null) fieldName = "this." + fieldName;
PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(fieldName + "= y;", null);
if (initializerCopy instanceof PsiArrayInitializerExpression) {
PsiType type = initializer.getType();
PsiNewExpression newExpression =
(PsiNewExpression)factory.createExpressionFromText("new " + type.getCanonicalText() + "{}", body);
newExpression.getArrayInitializer().replace(initializerCopy);
initializerCopy = newExpression;
}
((PsiAssignmentExpression)statement.getExpression()).getRExpression().replace(initializerCopy);
statement = (PsiExpressionStatement)field.getManager().getCodeStyleManager().reformat(statement);
body.add(statement);
initializer.delete();
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
private void addAssignmentToField(final String parameterName, final PsiMethod constructor) {
@NonNls String fieldName = convertToFieldName(parameterName);
final PsiManager manager = PsiManager.getInstance(myProject);
@@ -15,10 +15,12 @@
*/
package com.intellij.refactoring.move;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.Nullable;
@@ -30,6 +32,8 @@ import java.util.Set;
* @author ven
*/
public class MoveInstanceMembersUtil {
private static final Logger LOG = Logger.getInstance("#" + MoveInstanceMembersUtil.class.getName());
/**
* @param member nonstatic class member to search for class references in
* @return Set<PsiMember> in result map may be null in case no member is needed, but class itself is.
@@ -145,4 +149,32 @@ public class MoveInstanceMembersUtil {
}
return null;
}
public static void moveInitializerToConstructor(PsiElementFactory factory, PsiMethod constructor, PsiField field) {
final PsiExpression initializer = field.getInitializer();
PsiExpression initializerCopy = (PsiExpression)initializer.copy();
final PsiCodeBlock body = constructor.getBody();
if (body != null) {
try {
String fieldName = field.getName();
final PsiReferenceExpression refExpr = (PsiReferenceExpression)factory.createExpressionFromText(fieldName, body);
if (refExpr.resolve() != null) fieldName = "this." + fieldName;
PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(fieldName + "= y;", null);
if (initializerCopy instanceof PsiArrayInitializerExpression) {
PsiType type = initializer.getType();
PsiNewExpression newExpression =
(PsiNewExpression)factory.createExpressionFromText("new " + type.getCanonicalText() + "{}", body);
newExpression.getArrayInitializer().replace(initializerCopy);
initializerCopy = newExpression;
}
((PsiAssignmentExpression)statement.getExpression()).getRExpression().replace(initializerCopy);
statement = (PsiExpressionStatement)field.getManager().getCodeStyleManager().reformat(statement);
body.add(statement);
initializer.delete();
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
}
@@ -0,0 +1,7 @@
public class Extracted {
static final int[] myT;
public Extracted() {
this.myT = new int[]{Test.ii};
}
}
@@ -0,0 +1,3 @@
class Test {
static final int ii = 1;
}
@@ -0,0 +1,4 @@
class Test {
static final int ii = 1;
static final int[] myT = {ii};
}
@@ -0,0 +1,9 @@
public class Extracted {
private final Test test;
int[] myT;
public Extracted(Test test) {
this.test = test;
this.myT = new int[]{test.getIi()};
}
}
@@ -0,0 +1,7 @@
class Test {
int ii = 1;
public int getIi() {
return ii;
}
}
@@ -0,0 +1,4 @@
class Test {
int ii = 1;
int[] myT = {ii};
}
@@ -0,0 +1,3 @@
public class Extracted {
static final int[] myT = {1, 2};
}
@@ -0,0 +1,2 @@
class Test {
}
@@ -0,0 +1,3 @@
class Test {
static final int[] myT = {1,2};
}
@@ -1,5 +1,5 @@
public class Extracted {
private int myT;
int myT;
public int getMyT() {
return myT;
@@ -1,5 +1,5 @@
public class Extracted {
private int myT;
int myT;
public Extracted() {
}
@@ -1,6 +1,6 @@
public class Extracted<R> {
private final Test<R> test;
private R myT;
public R myT;
public Extracted(Test<R> test) {
this.test = test;
@@ -1,5 +1,5 @@
public class Extracted {
private int myT;
int myT;
public int getMyT() {
return myT;
@@ -1,5 +1,5 @@
public class Extracted {
private int myT;
int myT;
public Extracted() {
}
@@ -1,6 +1,6 @@
public class Extracted<T> {
private final Test<T> test;
private T myT;
T myT;
public Extracted(Test<T> test) {
this.test = test;
@@ -1,5 +1,5 @@
public class Extracted<T> {
private T myT;
public T myT;
public Extracted() {
}
@@ -1,5 +1,5 @@
public class Extracted<T> {
private T myT;
T myT;
public Extracted() {
}
@@ -71,6 +71,18 @@ public class ExtractClassTest extends MultiFileTestCase{
doTestFieldAndMethod();
}
public void testFieldInitializers() throws Exception {
doTestField(null);
}
public void testDependantFieldInitializers() throws Exception {
doTestField(null);
}
public void testDependantNonStaticFieldInitializers() throws Exception {
doTestField(null, true);
}
public void testInheritanceDelegation() throws Exception {
doTestMethod();
}