update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / memberPullUp / PullUpConflictsUtil.java
blob02e6171eed2c3fa25d7cce02f60f2c8ce5809b1c
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: 17.06.2002
21 * Time: 15:40:16
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;
49 import java.util.Set;
51 public class PullUpConflictsUtil {
52 private PullUpConflictsUtil() {}
54 public static MultiMap<PsiElement, String> checkConflicts(final MemberInfo[] infos,
55 PsiClass subclass,
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;
68 else {
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);
78 else {
79 abstractMethods.add((PsiMethod)member);
82 else {
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);
113 return 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())) {
124 String message =
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,
140 MemberInfo[] infos,
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;
157 if (isConflict) {
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) {
200 super(subclass);
201 myScope = scope;
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);
224 else {
225 isAccessible = classMember.hasModifierProperty(PsiModifier.PUBLIC);
227 if (!isAccessible) {
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);
235 return;
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();
255 return false;
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;