update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / memberPullUp / PullUpHelper.java
blob8faaec3eccc52133509c445c963d8aa1f5e8ea03
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
19 * User: dsl
20 * Date: 14.06.2002
21 * Time: 22:35:19
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com.intellij.refactoring.memberPullUp;
27 import com.intellij.codeInsight.AnnotationUtil;
28 import com.intellij.codeInsight.ChangeContextUtil;
29 import com.intellij.codeInsight.PsiEquivalenceUtil;
30 import com.intellij.codeInsight.intention.AddAnnotationFix;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.util.Condition;
33 import com.intellij.psi.*;
34 import com.intellij.psi.codeStyle.CodeStyleSettings;
35 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
36 import com.intellij.psi.search.LocalSearchScope;
37 import com.intellij.psi.search.searches.OverridingMethodsSearch;
38 import com.intellij.psi.search.searches.ReferencesSearch;
39 import com.intellij.psi.util.InheritanceUtil;
40 import com.intellij.psi.util.MethodSignatureUtil;
41 import com.intellij.psi.util.PsiTreeUtil;
42 import com.intellij.psi.util.PsiUtil;
43 import com.intellij.refactoring.listeners.JavaRefactoringListenerManager;
44 import com.intellij.refactoring.listeners.impl.JavaRefactoringListenerManagerImpl;
45 import com.intellij.refactoring.util.DocCommentPolicy;
46 import com.intellij.refactoring.util.RefactoringHierarchyUtil;
47 import com.intellij.refactoring.util.RefactoringUtil;
48 import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor;
49 import com.intellij.refactoring.util.classMembers.MemberInfo;
50 import com.intellij.util.IncorrectOperationException;
51 import com.intellij.util.VisibilityUtil;
52 import com.intellij.util.containers.HashMap;
53 import org.jetbrains.annotations.Nullable;
55 import java.util.*;
57 public class PullUpHelper {
58 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.memberPullUp.PullUpHelper");
59 private final PsiClass mySourceClass;
60 private final PsiClass myTargetSuperClass;
61 private final boolean myIsTargetInterface;
62 private final MemberInfo[] myMembersToMove;
63 private final DocCommentPolicy myJavaDocPolicy;
64 private HashSet<PsiMember> myMembersAfterMove = null;
65 private final PsiManager myManager;
68 public PullUpHelper(PsiClass sourceClass, PsiClass targetSuperClass, MemberInfo[] membersToMove,
69 DocCommentPolicy javaDocPolicy) {
70 mySourceClass = sourceClass;
71 myTargetSuperClass = targetSuperClass;
72 myMembersToMove = membersToMove;
73 myJavaDocPolicy = javaDocPolicy;
74 myIsTargetInterface = targetSuperClass.isInterface();
75 myManager = mySourceClass.getManager();
78 public void moveMembersToBase()
79 throws IncorrectOperationException {
80 final HashSet<PsiMember> movedMembers = new HashSet<PsiMember>();
81 myMembersAfterMove = new HashSet<PsiMember>();
83 // build aux sets
84 for (MemberInfo info : myMembersToMove) {
85 movedMembers.add(info.getMember());
88 // correct private member visibility
89 for (MemberInfo info : myMembersToMove) {
90 if (info.getMember() instanceof PsiClass && info.getOverrides() != null) continue;
91 PsiModifierListOwner modifierListOwner = info.getMember();
92 if (myIsTargetInterface) {
93 PsiUtil.setModifierProperty(modifierListOwner, PsiModifier.PUBLIC, true);
95 else if (modifierListOwner.hasModifierProperty(PsiModifier.PRIVATE)) {
96 if (info.isToAbstract() || willBeUsedInSubclass(modifierListOwner, movedMembers, myTargetSuperClass, mySourceClass)) {
97 PsiUtil.setModifierProperty(modifierListOwner, PsiModifier.PROTECTED, true);
100 ChangeContextUtil.encodeContextInfo(info.getMember(), true);
103 // do actual move
104 for (MemberInfo info : myMembersToMove) {
105 if (info.getMember() instanceof PsiMethod) {
106 PsiMethod method = (PsiMethod)info.getMember();
107 final boolean isOriginalMethodAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
108 if (myIsTargetInterface || info.isToAbstract()) {
109 PsiMethod methodCopy = (PsiMethod)method.copy();
110 ChangeContextUtil.clearContextInfo(method);
111 RefactoringUtil.abstractizeMethod(myTargetSuperClass, methodCopy);
113 myJavaDocPolicy.processCopiedJavaDoc(methodCopy.getDocComment(), method.getDocComment(), isOriginalMethodAbstract);
115 final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(methodCopy);
116 CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(method.getProject());
117 if (styleSettings.INSERT_OVERRIDE_ANNOTATION) {
118 if (PsiUtil.isLanguageLevel5OrHigher(mySourceClass) && !myTargetSuperClass.isInterface() || PsiUtil.isLanguageLevel6OrHigher(mySourceClass)) {
119 new AddAnnotationFix("java.lang.Override", method).invoke(method.getProject(), null, mySourceClass.getContainingFile());
122 if (isOriginalMethodAbstract && myTargetSuperClass.isInterface() && !PsiUtil.isLanguageLevel6OrHigher(mySourceClass)) {
123 for (PsiMethod oMethod : OverridingMethodsSearch.search(method)) {
124 final PsiAnnotation annotation = AnnotationUtil.findAnnotation(oMethod, Override.class.getName());
125 if (annotation != null) {
126 annotation.delete();
130 myMembersAfterMove.add(movedElement);
131 if (isOriginalMethodAbstract) {
132 method.delete();
135 else {
136 if (isOriginalMethodAbstract) {
137 PsiUtil.setModifierProperty(myTargetSuperClass, PsiModifier.ABSTRACT, true);
139 fixReferencesToStatic(method, movedMembers);
140 final PsiMethod superClassMethod = myTargetSuperClass.findMethodBySignature(method, false);
141 if (superClassMethod != null && superClassMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
142 superClassMethod.replace(method);
144 else {
145 final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(method);
146 myMembersAfterMove.add(movedElement);
148 method.delete();
151 else if (info.getMember() instanceof PsiField) {
152 PsiField field = (PsiField)info.getMember();
153 field.normalizeDeclaration();
154 fixReferencesToStatic(field, movedMembers);
155 if (myIsTargetInterface) {
156 PsiUtil.setModifierProperty(field, PsiModifier.PUBLIC, true);
158 final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(field);
159 myMembersAfterMove.add(movedElement);
160 field.delete();
162 else if (info.getMember() instanceof PsiClass) {
163 PsiClass aClass = (PsiClass)info.getMember();
164 if (Boolean.FALSE.equals(info.getOverrides())) {
165 final PsiReferenceList sourceReferenceList = info.getSourceReferenceList();
166 LOG.assertTrue(sourceReferenceList != null);
167 PsiJavaCodeReferenceElement ref = mySourceClass.equals(sourceReferenceList.getParent()) ?
168 RefactoringUtil.removeFromReferenceList(sourceReferenceList, aClass) :
169 RefactoringUtil.findReferenceToClass(sourceReferenceList, aClass);
170 if (ref != null) {
171 final PsiReferenceList referenceList =
172 myTargetSuperClass.isInterface() ? myTargetSuperClass.getExtendsList() : myTargetSuperClass.getImplementsList();
173 assert referenceList != null;
174 referenceList.add(ref);
177 else {
178 fixReferencesToStatic(aClass, movedMembers);
179 final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(aClass);
180 myMembersAfterMove.add(movedElement);
181 aClass.delete();
186 ExplicitSuperDeleter explicitSuperDeleter = new ExplicitSuperDeleter();
187 for (PsiMember member : myMembersAfterMove) {
188 member.accept(explicitSuperDeleter);
190 explicitSuperDeleter.fixSupers();
192 final QualifiedThisSuperAdjuster qualifiedThisSuperAdjuster = new QualifiedThisSuperAdjuster();
193 for (PsiMember member : myMembersAfterMove) {
194 member.accept(qualifiedThisSuperAdjuster);
197 ChangeContextUtil.decodeContextInfo(myTargetSuperClass, null, null);
199 for (final PsiMember movedMember : myMembersAfterMove) {
200 final JavaRefactoringListenerManager listenerManager = JavaRefactoringListenerManager.getInstance(movedMember.getProject());
201 ((JavaRefactoringListenerManagerImpl)listenerManager).fireMemberMoved(mySourceClass, movedMember);
205 public void moveFieldInitializations() throws IncorrectOperationException {
206 LOG.assertTrue(myMembersAfterMove != null);
208 final LinkedHashSet<PsiField> movedFields = new LinkedHashSet<PsiField>();
209 for (PsiMember member : myMembersAfterMove) {
210 if (member instanceof PsiField) {
211 movedFields.add((PsiField)member);
215 if (movedFields.isEmpty()) return;
216 PsiMethod[] constructors = myTargetSuperClass.getConstructors();
218 if (constructors.length == 0) {
219 constructors = new PsiMethod[]{null};
222 HashMap<PsiMethod,HashSet<PsiMethod>> constructorsToSubConstructors = buildConstructorsToSubConstructorsMap(constructors);
223 for (PsiMethod constructor : constructors) {
224 HashSet<PsiMethod> subConstructors = constructorsToSubConstructors.get(constructor);
225 tryToMoveInitializers(constructor, subConstructors, movedFields);
229 private static class Initializer {
230 public final PsiStatement initializer;
231 public final Set<PsiField> movedFieldsUsed;
232 public final Set<PsiParameter> usedParameters;
233 public final List<PsiElement> statementsToRemove;
235 private Initializer(PsiStatement initializer, Set<PsiField> movedFieldsUsed, Set<PsiParameter> usedParameters, List<PsiElement> statementsToRemove) {
236 this.initializer = initializer;
237 this.movedFieldsUsed = movedFieldsUsed;
238 this.statementsToRemove = statementsToRemove;
239 this.usedParameters = usedParameters;
243 private void tryToMoveInitializers(PsiMethod constructor, HashSet<PsiMethod> subConstructors, LinkedHashSet<PsiField> movedFields) throws IncorrectOperationException {
244 final LinkedHashMap<PsiField, Initializer> fieldsToInitializers = new LinkedHashMap<PsiField, Initializer>();
245 boolean anyFound = false;
247 for (PsiField field : movedFields) {
248 PsiStatement commonInitializer = null;
249 final ArrayList<PsiElement> fieldInitializersToRemove = new ArrayList<PsiElement>();
250 for (PsiMethod subConstructor : subConstructors) {
251 commonInitializer = hasCommonInitializer(commonInitializer, subConstructor, field, fieldInitializersToRemove);
252 if (commonInitializer == null) break;
254 if (commonInitializer != null) {
255 final ParametersAndMovedFieldsUsedCollector visitor = new ParametersAndMovedFieldsUsedCollector(movedFields);
256 commonInitializer.accept(visitor);
257 fieldsToInitializers.put(field, new Initializer(commonInitializer,
258 visitor.getUsedFields(), visitor.getUsedParameters(), fieldInitializersToRemove));
259 anyFound = true;
263 if (!anyFound) return;
268 final Set<PsiField> initializedFields = fieldsToInitializers.keySet();
269 Set<PsiField> unmovable = RefactoringUtil.transitiveClosure(
270 new RefactoringUtil.Graph<PsiField>() {
271 public Set<PsiField> getVertices() {
272 return initializedFields;
275 public Set<PsiField> getTargets(PsiField source) {
276 return fieldsToInitializers.get(source).movedFieldsUsed;
279 new Condition<PsiField>() {
280 public boolean value(PsiField object) {
281 return !initializedFields.contains(object);
286 for (PsiField psiField : unmovable) {
287 fieldsToInitializers.remove(psiField);
291 final PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
293 if (constructor == null) {
294 constructor = (PsiMethod) myTargetSuperClass.add(factory.createConstructor());
295 final String visibilityModifier = VisibilityUtil.getVisibilityModifier(myTargetSuperClass.getModifierList());
296 PsiUtil.setModifierProperty(constructor, visibilityModifier, true);
300 ArrayList<PsiField> initializedFields = new ArrayList<PsiField>(fieldsToInitializers.keySet());
302 Collections.sort(initializedFields, new Comparator<PsiField>() {
303 public int compare(PsiField field1, PsiField field2) {
304 Initializer i1 = fieldsToInitializers.get(field1);
305 Initializer i2 = fieldsToInitializers.get(field2);
306 if(i1.movedFieldsUsed.contains(field2)) return 1;
307 if(i2.movedFieldsUsed.contains(field1)) return -1;
308 return 0;
312 for (final PsiField initializedField : initializedFields) {
313 Initializer initializer = fieldsToInitializers.get(initializedField);
315 //correct constructor parameters and subConstructors super calls
316 final PsiParameterList parameterList = constructor.getParameterList();
317 for (final PsiParameter parameter : initializer.usedParameters) {
318 parameterList.add(parameter);
321 for (final PsiMethod subConstructor : subConstructors) {
322 modifySuperCall(subConstructor, initializer.usedParameters);
325 PsiStatement assignmentStatement = (PsiStatement)constructor.getBody().add(initializer.initializer);
327 ChangeContextUtil.decodeContextInfo(assignmentStatement,
328 myTargetSuperClass, RefactoringUtil.createThisExpression(myManager, null));
329 for (PsiElement psiElement : initializer.statementsToRemove) {
330 psiElement.delete();
335 private static void modifySuperCall(final PsiMethod subConstructor, final Set<PsiParameter> parametersToPassToSuper) {
336 final PsiCodeBlock body = subConstructor.getBody();
337 if (body != null) {
338 PsiMethodCallExpression superCall = null;
339 final PsiStatement[] statements = body.getStatements();
340 if (statements.length > 0) {
341 if (statements[0] instanceof PsiExpressionStatement) {
342 final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
343 if (expression instanceof PsiMethodCallExpression) {
344 final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
345 if ("super".equals(methodCall.getMethodExpression().getText())) {
346 superCall = methodCall;
352 final PsiElementFactory factory = JavaPsiFacade.getInstance(subConstructor.getProject()).getElementFactory();
353 try {
354 if (superCall == null) {
355 PsiExpressionStatement statement =
356 (PsiExpressionStatement)factory.createStatementFromText("super();", null);
357 statement = (PsiExpressionStatement)body.addAfter(statement, null);
358 superCall = (PsiMethodCallExpression)statement.getExpression();
361 final PsiExpressionList argList = superCall.getArgumentList();
362 for (final PsiParameter parameter : parametersToPassToSuper) {
363 argList.add(factory.createExpressionFromText(parameter.getName(), null));
366 catch (IncorrectOperationException e) {
367 LOG.error(e);
372 @Nullable
373 private PsiStatement hasCommonInitializer(PsiStatement commonInitializer, PsiMethod subConstructor, PsiField field, ArrayList<PsiElement> statementsToRemove) {
374 final PsiCodeBlock body = subConstructor.getBody();
375 if (body == null) return null;
376 final PsiStatement[] statements = body.getStatements();
378 // Algorithm: there should be only one write usage of field in a subConstructor,
379 // and in that usage field must be a target of top-level assignment, and RHS of assignment
380 // should be the same as commonInitializer if latter is non-null.
382 // There should be no usages before that initializer, and there should be
383 // no write usages afterwards.
384 PsiStatement commonInitializerCandidate = null;
385 for (PsiStatement statement : statements) {
386 final HashSet<PsiStatement> collectedStatements = new HashSet<PsiStatement>();
387 collectPsiStatements(statement, collectedStatements);
388 boolean doLookup = true;
389 for (PsiStatement collectedStatement : collectedStatements) {
390 if (collectedStatement instanceof PsiExpressionStatement) {
391 final PsiExpression expression = ((PsiExpressionStatement)collectedStatement).getExpression();
392 if (expression instanceof PsiAssignmentExpression) {
393 final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression;
394 final PsiExpression lExpression = assignmentExpression.getLExpression();
395 if (lExpression instanceof PsiReferenceExpression) {
396 final PsiReferenceExpression lRef = (PsiReferenceExpression)lExpression;
397 if (lRef.getQualifierExpression() == null || lRef.getQualifierExpression() instanceof PsiThisExpression) {
398 final PsiElement resolved = lRef.resolve();
399 if (resolved == field) {
400 doLookup = false;
401 if (commonInitializerCandidate == null) {
402 final PsiExpression initializer = assignmentExpression.getRExpression();
403 if (initializer == null) return null;
404 if (commonInitializer == null) {
405 final IsMovableInitializerVisitor visitor = new IsMovableInitializerVisitor();
406 statement.accept(visitor);
407 if (visitor.isMovable()) {
408 ChangeContextUtil.encodeContextInfo(statement, true);
409 PsiStatement statementCopy = (PsiStatement)statement.copy();
410 ChangeContextUtil.clearContextInfo(statement);
411 statementsToRemove.add(statement);
412 commonInitializerCandidate = statementCopy;
414 else {
415 return null;
418 else {
419 if (PsiEquivalenceUtil.areElementsEquivalent(commonInitializer, statement)) {
420 statementsToRemove.add(statement);
421 commonInitializerCandidate = commonInitializer;
423 else {
424 return null;
428 else if (!PsiEquivalenceUtil.areElementsEquivalent(commonInitializerCandidate, statement)){
429 return null;
437 if (doLookup) {
438 final PsiReference[] references =
439 ReferencesSearch.search(field, new LocalSearchScope(statement), false).toArray(new PsiReference[0]);
440 if (commonInitializerCandidate == null && references.length > 0) {
441 return null;
444 for (PsiReference reference : references) {
445 if (RefactoringUtil.isAssignmentLHS(reference.getElement())) return null;
449 return commonInitializerCandidate;
452 private static void collectPsiStatements(PsiElement root, Set<PsiStatement> collected) {
453 if (root instanceof PsiStatement){
454 collected.add((PsiStatement)root);
457 for (PsiElement element : root.getChildren()) {
458 collectPsiStatements(element, collected);
462 private static class ParametersAndMovedFieldsUsedCollector extends JavaRecursiveElementWalkingVisitor {
463 private final Set<PsiField> myMovedFields;
464 private final Set<PsiField> myUsedFields;
466 private final Set<PsiParameter> myUsedParameters = new HashSet<PsiParameter>();
468 private ParametersAndMovedFieldsUsedCollector(HashSet<PsiField> movedFields) {
469 myMovedFields = movedFields;
470 myUsedFields = new HashSet<PsiField>();
473 public Set<PsiParameter> getUsedParameters() {
474 return myUsedParameters;
477 public Set<PsiField> getUsedFields() {
478 return myUsedFields;
481 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
482 final PsiExpression qualifierExpression = expression.getQualifierExpression();
483 if (qualifierExpression != null
484 && !(qualifierExpression instanceof PsiThisExpression)) {
485 return;
487 final PsiElement resolved = expression.resolve();
488 if (resolved instanceof PsiParameter) {
489 myUsedParameters.add((PsiParameter)resolved);
490 } else if (myMovedFields.contains(resolved)) {
491 myUsedFields.add((PsiField)resolved);
496 private class IsMovableInitializerVisitor extends JavaRecursiveElementWalkingVisitor {
497 private boolean myIsMovable = true;
499 public boolean isMovable() {
500 return myIsMovable;
503 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
504 visitReferenceElement(expression);
507 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement referenceElement) {
508 if (!myIsMovable) return;
509 final PsiExpression qualifier;
510 if (referenceElement instanceof PsiReferenceExpression) {
511 qualifier = ((PsiReferenceExpression) referenceElement).getQualifierExpression();
512 } else {
513 qualifier = null;
515 if (qualifier == null || qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
516 final PsiElement resolved = referenceElement.resolve();
517 if (!(resolved instanceof PsiParameter)) {
518 if (resolved instanceof PsiClass && (((PsiClass) resolved).hasModifierProperty(PsiModifier.STATIC) || ((PsiClass)resolved).getContainingClass() == null)) {
519 return;
521 PsiClass containingClass = null;
522 if (resolved instanceof PsiMember && !((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC)) {
523 containingClass = ((PsiMember) resolved).getContainingClass();
525 myIsMovable = containingClass != null && InheritanceUtil.isInheritorOrSelf(myTargetSuperClass, containingClass, true);
527 } else {
528 qualifier.accept(this);
532 @Override public void visitElement(PsiElement element) {
533 if (myIsMovable) {
534 super.visitElement(element);
539 private HashMap<PsiMethod,HashSet<PsiMethod>> buildConstructorsToSubConstructorsMap(final PsiMethod[] constructors) {
540 final HashMap<PsiMethod,HashSet<PsiMethod>> constructorsToSubConstructors = new HashMap<PsiMethod, HashSet<PsiMethod>>();
541 for (PsiMethod constructor : constructors) {
542 final HashSet<PsiMethod> referencingSubConstructors = new HashSet<PsiMethod>();
543 constructorsToSubConstructors.put(constructor, referencingSubConstructors);
544 if (constructor != null) {
545 // find references
546 for (PsiReference reference : ReferencesSearch.search(constructor, new LocalSearchScope(mySourceClass), false)) {
547 final PsiElement element = reference.getElement();
548 if (element != null && "super".equals(element.getText())) {
549 PsiMethod parentMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
550 if (parentMethod != null && parentMethod.isConstructor()) {
551 referencingSubConstructors.add(parentMethod);
557 // check default constructor
558 if (constructor == null || constructor.getParameterList().getParametersCount() == 0) {
559 RefactoringUtil.visitImplicitSuperConstructorUsages(mySourceClass, new RefactoringUtil.ImplicitConstructorUsageVisitor() {
560 public void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor) {
561 referencingSubConstructors.add(constructor);
564 public void visitClassWithoutConstructors(PsiClass aClass) {
566 }, myTargetSuperClass);
570 return constructorsToSubConstructors;
573 private void fixReferencesToStatic(PsiElement classMember, Set<PsiMember> movedMembers) throws IncorrectOperationException {
574 StaticReferencesCollector collector = new StaticReferencesCollector(movedMembers);
575 classMember.accept(collector);
576 ArrayList<PsiJavaCodeReferenceElement> refs = collector.getReferences();
577 ArrayList<PsiElement> members = collector.getReferees();
578 ArrayList<PsiClass> classes = collector.getRefereeClasses();
579 PsiElementFactory factory = JavaPsiFacade.getInstance(classMember.getProject()).getElementFactory();
581 for (int i = 0; i < refs.size(); i++) {
582 PsiJavaCodeReferenceElement ref = refs.get(i);
583 PsiElement namedElement = members.get(i);
584 PsiClass aClass = classes.get(i);
586 if (namedElement instanceof PsiNamedElement) {
587 PsiReferenceExpression newRef =
588 (PsiReferenceExpression) factory.createExpressionFromText
589 ("a." + ((PsiNamedElement) namedElement).getName(),
590 null);
591 final PsiExpression qualifierExpression = newRef.getQualifierExpression();
592 assert qualifierExpression != null;
593 qualifierExpression.replace(factory.createReferenceExpression(aClass));
594 ref.replace(newRef);
599 private class StaticReferencesCollector extends ClassMemberReferencesVisitor {
600 ArrayList<PsiJavaCodeReferenceElement> myReferences;
601 ArrayList<PsiElement> myReferees;
602 ArrayList<PsiClass> myRefereeClasses;
603 private final Set<PsiMember> myMovedMembers;
605 private StaticReferencesCollector(Set<PsiMember> movedMembers) {
606 super(mySourceClass);
607 myMovedMembers = movedMembers;
608 myReferees = new ArrayList<PsiElement>();
609 myRefereeClasses = new ArrayList<PsiClass>();
610 myReferences = new ArrayList<PsiJavaCodeReferenceElement>();
613 public ArrayList<PsiElement> getReferees() {
614 return myReferees;
617 public ArrayList<PsiClass> getRefereeClasses() {
618 return myRefereeClasses;
621 public ArrayList<PsiJavaCodeReferenceElement> getReferences() {
622 return myReferences;
625 protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) {
626 if (classMember instanceof PsiClass) return;
627 if (classMember.hasModifierProperty(PsiModifier.STATIC)) {
628 if (!myMovedMembers.contains(classMember) &&
629 RefactoringHierarchyUtil.isMemberBetween(myTargetSuperClass, mySourceClass, classMember)) {
630 myReferences.add(classMemberReference);
631 myReferees.add(classMember);
632 myRefereeClasses.add(classMember.getContainingClass());
634 else if ((myMovedMembers.contains(classMember) || myMembersAfterMove.contains(classMember)) && classMemberReference.isQualified()) {
635 myReferences.add(classMemberReference);
636 myReferees.add(classMember);
637 myRefereeClasses.add(myTargetSuperClass);
643 private class QualifiedThisSuperAdjuster extends JavaRecursiveElementVisitor {
644 @Override public void visitThisExpression(PsiThisExpression expression) {
645 super.visitThisExpression(expression);
646 final PsiJavaCodeReferenceElement qualifier = expression.getQualifier();
647 if (qualifier != null && qualifier.isReferenceTo(mySourceClass)) {
648 try {
649 qualifier.bindToElement(myTargetSuperClass);
651 catch (IncorrectOperationException e) {
652 LOG.error(e);
657 @Override public void visitSuperExpression(PsiSuperExpression expression) {
658 super.visitSuperExpression(expression);
659 final PsiJavaCodeReferenceElement qualifier = expression.getQualifier();
660 if (qualifier != null && qualifier.isReferenceTo(mySourceClass)) {
661 try {
662 expression.replace(JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createExpressionFromText(myTargetSuperClass.getName() + ".this", null));
664 catch (IncorrectOperationException e) {
665 LOG.error(e);
671 private class ExplicitSuperDeleter extends JavaRecursiveElementWalkingVisitor {
672 private final ArrayList<PsiExpression> mySupersToDelete = new ArrayList<PsiExpression>();
673 private final ArrayList<PsiSuperExpression> mySupersToChangeToThis = new ArrayList<PsiSuperExpression>();
675 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
676 if(expression.getQualifierExpression() instanceof PsiSuperExpression) {
677 PsiElement resolved = expression.resolve();
678 if (resolved == null || resolved instanceof PsiMethod && shouldFixSuper((PsiMethod) resolved)) {
679 mySupersToDelete.add(expression.getQualifierExpression());
684 @Override public void visitSuperExpression(PsiSuperExpression expression) {
685 mySupersToChangeToThis.add(expression);
688 @Override public void visitClass(PsiClass aClass) {
689 // do nothing
692 private boolean shouldFixSuper(PsiMethod method) {
693 for (PsiMember element : myMembersAfterMove) {
694 if (element instanceof PsiMethod) {
695 PsiMethod member = (PsiMethod)element;
696 // if there is such member among moved members, super qualifier
697 // should not be removed
698 final PsiManager manager = method.getManager();
699 if (manager.areElementsEquivalent(member.getContainingClass(), method.getContainingClass()) &&
700 MethodSignatureUtil.areSignaturesEqual(member, method)) {
701 return false;
706 final PsiMethod methodFromSuper = myTargetSuperClass.findMethodBySignature(method, false);
707 return methodFromSuper == null;
710 public void fixSupers() throws IncorrectOperationException {
711 final PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
712 PsiThisExpression thisExpression = (PsiThisExpression) factory.createExpressionFromText("this", null);
713 for (PsiExpression psiExpression : mySupersToDelete) {
714 psiExpression.delete();
717 for (PsiSuperExpression psiSuperExpression : mySupersToChangeToThis) {
718 psiSuperExpression.replace(thisExpression);
723 private static boolean willBeUsedInSubclass(PsiElement member, Set<PsiMember> movedMembers, PsiClass superclass, PsiClass subclass) {
724 for (PsiReference ref : ReferencesSearch.search(member, new LocalSearchScope(subclass), false)) {
725 PsiElement element = ref.getElement();
726 if (!RefactoringHierarchyUtil.willBeInTargetClass(element, movedMembers, superclass, false)) {
727 return true;
730 return false;
733 public static boolean checkedInterfacesContain(Collection<MemberInfo> memberInfos, PsiMethod psiMethod) {
734 for (MemberInfo memberInfo : memberInfos) {
735 if (memberInfo.isChecked() &&
736 memberInfo.getMember() instanceof PsiClass &&
737 Boolean.FALSE.equals(memberInfo.getOverrides())) {
738 if (((PsiClass)memberInfo.getMember()).findMethodBySignature(psiMethod, true) != null) {
739 return true;
743 return false;