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.
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
.psi
.*;
28 import com
.intellij
.psi
.search
.searches
.ClassInheritorsSearch
;
29 import com
.intellij
.psi
.util
.MethodSignature
;
30 import com
.intellij
.psi
.util
.MethodSignatureUtil
;
31 import com
.intellij
.psi
.util
.PsiUtil
;
32 import com
.intellij
.psi
.util
.TypeConversionUtil
;
33 import com
.intellij
.refactoring
.RefactoringBundle
;
34 import com
.intellij
.refactoring
.util
.CommonRefactoringUtil
;
35 import com
.intellij
.refactoring
.util
.RefactoringConflictsUtil
;
36 import com
.intellij
.refactoring
.util
.RefactoringHierarchyUtil
;
37 import com
.intellij
.refactoring
.util
.RefactoringUIUtil
;
38 import com
.intellij
.refactoring
.util
.classMembers
.ClassMemberReferencesVisitor
;
39 import com
.intellij
.refactoring
.util
.classMembers
.InterfaceContainmentVerifier
;
40 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfo
;
41 import com
.intellij
.usageView
.UsageInfo
;
42 import com
.intellij
.util
.VisibilityUtil
;
43 import com
.intellij
.util
.containers
.MultiMap
;
44 import org
.jetbrains
.annotations
.Nullable
;
46 import java
.util
.ArrayList
;
47 import java
.util
.HashSet
;
48 import java
.util
.List
;
51 public class PullUpConflictsUtil
{
52 private PullUpConflictsUtil() {}
54 public static MultiMap
<PsiElement
, String
> checkConflicts(final MemberInfo
[] infos
,
56 @Nullable PsiClass superClass
,
57 PsiPackage targetPackage
,
58 PsiDirectory targetDirectory
,
59 final InterfaceContainmentVerifier interfaceContainmentVerifier
) {
60 final Set
<PsiMember
> movedMembers
= new HashSet
<PsiMember
>();
61 final Set
<PsiMethod
> abstractMethods
= new HashSet
<PsiMethod
>();
62 final boolean isInterfaceTarget
;
63 final PsiElement targetRepresentativeElement
;
64 if (superClass
!= null) {
65 isInterfaceTarget
= superClass
.isInterface();
66 targetRepresentativeElement
= superClass
;
69 isInterfaceTarget
= false;
70 targetRepresentativeElement
= targetDirectory
;
72 for (MemberInfo info
: infos
) {
73 PsiMember member
= info
.getMember();
74 if (member
instanceof PsiMethod
) {
75 if (!info
.isToAbstract() && !isInterfaceTarget
) {
76 movedMembers
.add(member
);
79 abstractMethods
.add((PsiMethod
)member
);
83 movedMembers
.add(member
);
86 final MultiMap
<PsiElement
, String
> conflicts
= new MultiMap
<PsiElement
, String
>();
87 RefactoringConflictsUtil
.analyzeAccessibilityConflicts(movedMembers
, superClass
, conflicts
, null, targetRepresentativeElement
);
88 if (superClass
!= null) {
89 checkSuperclassMembers(superClass
, infos
, conflicts
);
90 if (isInterfaceTarget
) {
91 checkInterfaceTarget(infos
, conflicts
);
94 // check if moved methods use other members in the classes between Subclass and Superclass
95 List
<PsiElement
> checkModuleConflictsList
= new ArrayList
<PsiElement
>();
96 for (PsiMember member
: movedMembers
) {
97 if (member
instanceof PsiMethod
|| member
instanceof PsiClass
&& !(member
instanceof PsiCompiledElement
)) {
98 ConflictingUsagesOfSubClassMembers visitor
=
99 new ConflictingUsagesOfSubClassMembers(member
, movedMembers
, abstractMethods
, subclass
, superClass
,
100 superClass
!= null ?
null : targetPackage
, conflicts
,
101 interfaceContainmentVerifier
);
102 member
.accept(visitor
);
104 checkModuleConflictsList
.add(member
);
106 for (final PsiMethod method
: abstractMethods
) {
107 checkModuleConflictsList
.add(method
.getParameterList());
108 checkModuleConflictsList
.add(method
.getReturnTypeElement());
109 checkModuleConflictsList
.add(method
.getTypeParameterList());
111 RefactoringConflictsUtil
.analyzeModuleConflicts(subclass
.getProject(), checkModuleConflictsList
,
112 new UsageInfo
[0], targetRepresentativeElement
, conflicts
);
116 private static void checkInterfaceTarget(MemberInfo
[] infos
, MultiMap
<PsiElement
, String
> conflictsList
) {
117 for (MemberInfo info
: infos
) {
118 PsiElement member
= info
.getMember();
120 if (member
instanceof PsiField
|| member
instanceof PsiClass
) {
122 if (!((PsiModifierListOwner
)member
).hasModifierProperty(PsiModifier
.STATIC
)
123 && !(member
instanceof PsiClass
&& ((PsiClass
)member
).isInterface())) {
125 RefactoringBundle
.message("0.is.not.static.it.cannot.be.moved.to.the.interface", RefactoringUIUtil
.getDescription(member
, false));
126 message
= CommonRefactoringUtil
.capitalize(message
);
127 conflictsList
.putValue(member
, message
);
131 if (member
instanceof PsiField
&& ((PsiField
)member
).getInitializer() == null) {
132 String message
= RefactoringBundle
.message("0.is.not.initialized.in.declaration.such.fields.are.not.allowed.in.interfaces",
133 RefactoringUIUtil
.getDescription(member
, false));
134 conflictsList
.putValue(member
, CommonRefactoringUtil
.capitalize(message
));
139 private static void checkSuperclassMembers(PsiClass superClass
,
141 MultiMap
<PsiElement
, String
> conflictsList
) {
142 for (MemberInfo info
: infos
) {
143 PsiMember member
= info
.getMember();
144 boolean isConflict
= false;
145 if (member
instanceof PsiField
) {
146 String name
= member
.getName();
148 isConflict
= superClass
.findFieldByName(name
, false) != null;
150 else if (member
instanceof PsiMethod
) {
151 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getSuperClassSubstitutor(superClass
, member
.getContainingClass(), PsiSubstitutor
.EMPTY
);
152 MethodSignature signature
= ((PsiMethod
) member
).getSignature(superSubstitutor
);
153 final PsiMethod superClassMethod
= MethodSignatureUtil
.findMethodBySignature(superClass
, signature
, false);
154 isConflict
= superClassMethod
!= null;
158 String message
= RefactoringBundle
.message("0.already.contains.a.1",
159 RefactoringUIUtil
.getDescription(superClass
, false),
160 RefactoringUIUtil
.getDescription(member
, false));
161 message
= CommonRefactoringUtil
.capitalize(message
);
162 conflictsList
.putValue(superClass
, message
);
165 if (member
instanceof PsiMethod
) {
166 final PsiMethod method
= (PsiMethod
)member
;
167 final PsiModifierList modifierList
= method
.getModifierList();
168 if (!modifierList
.hasModifierProperty(PsiModifier
.PRIVATE
)) {
169 for (PsiClass subClass
: ClassInheritorsSearch
.search(superClass
)) {
170 if (method
.getContainingClass() != subClass
) {
171 MethodSignature signature
= ((PsiMethod
) member
).getSignature(TypeConversionUtil
.getSuperClassSubstitutor(superClass
, subClass
, PsiSubstitutor
.EMPTY
));
172 final PsiMethod wouldBeOverriden
= MethodSignatureUtil
.findMethodBySignature(subClass
, signature
, false);
173 if (wouldBeOverriden
!= null && VisibilityUtil
.compare(VisibilityUtil
.getVisibilityModifier(wouldBeOverriden
.getModifierList()),
174 VisibilityUtil
.getVisibilityModifier(modifierList
)) > 0) {
175 conflictsList
.putValue(wouldBeOverriden
, CommonRefactoringUtil
.capitalize(RefactoringUIUtil
.getDescription(method
, true) + " in super class would clash with local method from " + RefactoringUIUtil
.getDescription(subClass
, true)));
185 private static class ConflictingUsagesOfSubClassMembers
extends ClassMemberReferencesVisitor
{
186 private final PsiElement myScope
;
187 private final Set
<PsiMember
> myMovedMembers
;
188 private final Set
<PsiMethod
> myAbstractMethods
;
189 private final PsiClass mySubclass
;
190 private final PsiClass mySuperClass
;
191 private final PsiPackage myTargetPackage
;
192 private final MultiMap
<PsiElement
, String
> myConflictsList
;
193 private final InterfaceContainmentVerifier myInterfaceContainmentVerifier
;
195 ConflictingUsagesOfSubClassMembers(PsiElement scope
,
196 Set
<PsiMember
> movedMembers
, Set
<PsiMethod
> abstractMethods
,
197 PsiClass subclass
, PsiClass superClass
,
198 PsiPackage targetPackage
, MultiMap
<PsiElement
, String
> conflictsList
,
199 InterfaceContainmentVerifier interfaceContainmentVerifier
) {
202 myMovedMembers
= movedMembers
;
203 myAbstractMethods
= abstractMethods
;
204 mySubclass
= subclass
;
205 mySuperClass
= superClass
;
206 myTargetPackage
= targetPackage
;
207 myConflictsList
= conflictsList
;
208 myInterfaceContainmentVerifier
= interfaceContainmentVerifier
;
211 protected void visitClassMemberReferenceElement(PsiMember classMember
,
212 PsiJavaCodeReferenceElement classMemberReference
) {
213 if (classMember
!= null
214 && RefactoringHierarchyUtil
.isMemberBetween(mySuperClass
, mySubclass
, classMember
)) {
215 if (classMember
.hasModifierProperty(PsiModifier
.STATIC
)
216 && !willBeMoved(classMember
)) {
217 final boolean isAccessible
;
218 if (mySuperClass
!= null) {
219 isAccessible
= PsiUtil
.isAccessible(classMember
, mySuperClass
, null);
221 else if (myTargetPackage
!= null) {
222 isAccessible
= PsiUtil
.isAccessibleFromPackage(classMember
, myTargetPackage
);
225 isAccessible
= classMember
.hasModifierProperty(PsiModifier
.PUBLIC
);
228 String message
= RefactoringBundle
.message("0.uses.1.which.is.not.accessible.from.the.superclass",
229 RefactoringUIUtil
.getDescription(myScope
, false),
230 RefactoringUIUtil
.getDescription(classMember
, true));
231 message
= CommonRefactoringUtil
.capitalize(message
);
232 myConflictsList
.putValue(classMember
, message
);
237 if (!myAbstractMethods
.contains(classMember
) && !willBeMoved(classMember
)) {
238 if (!existsInSuperClass(classMember
)) {
239 String message
= RefactoringBundle
.message("0.uses.1.which.is.not.moved.to.the.superclass",
240 RefactoringUIUtil
.getDescription(myScope
, false),
241 RefactoringUIUtil
.getDescription(classMember
, true));
242 message
= CommonRefactoringUtil
.capitalize(message
);
243 myConflictsList
.putValue(classMember
, message
);
249 private boolean willBeMoved(PsiElement element
) {
250 PsiElement parent
= element
;
251 while (parent
!= null) {
252 if (myMovedMembers
.contains(parent
)) return true;
253 parent
= parent
.getParent();
258 private boolean existsInSuperClass(PsiElement classMember
) {
259 if (!(classMember
instanceof PsiMethod
)) return false;
260 final PsiMethod method
= ((PsiMethod
)classMember
);
261 if (myInterfaceContainmentVerifier
.checkedInterfacesContain(method
)) return true;
262 if (mySuperClass
== null) return false;
263 final PsiMethod methodBySignature
= mySuperClass
.findMethodBySignature(method
, true);
264 return methodBySignature
!= null;