mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
extract class: field initializers fixed (IDEA-52622)
This commit is contained in:
+58
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-109
@@ -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();
|
||||
|
||||
+2
-29
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
public class Extracted {
|
||||
static final int[] myT;
|
||||
|
||||
public Extracted() {
|
||||
this.myT = new int[]{Test.ii};
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class Test {
|
||||
static final int ii = 1;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class Test {
|
||||
static final int ii = 1;
|
||||
static final int[] myT = {ii};
|
||||
}
|
||||
+9
@@ -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()};
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
int ii = 1;
|
||||
|
||||
public int getIi() {
|
||||
return ii;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class Test {
|
||||
int ii = 1;
|
||||
int[] myT = {ii};
|
||||
}
|
||||
+3
@@ -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
-1
@@ -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
-1
@@ -1,5 +1,5 @@
|
||||
public class Extracted {
|
||||
private int myT;
|
||||
int myT;
|
||||
|
||||
public int getMyT() {
|
||||
return myT;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
public class Extracted {
|
||||
private int myT;
|
||||
int myT;
|
||||
|
||||
public Extracted() {
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
-1
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user