update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / rename / RenameJavaMethodProcessor.java
blob84cbf69b99952f3aef9e17efb96734cc7b8fc99b
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.
16 package com.intellij.refactoring.rename;
18 import com.intellij.ide.util.SuperMethodWarningUtil;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.psi.*;
22 import com.intellij.psi.search.GlobalSearchScope;
23 import com.intellij.psi.search.searches.ClassInheritorsSearch;
24 import com.intellij.psi.search.searches.MethodReferencesSearch;
25 import com.intellij.psi.search.searches.OverridingMethodsSearch;
26 import com.intellij.psi.util.MethodSignature;
27 import com.intellij.psi.util.MethodSignatureUtil;
28 import com.intellij.psi.util.PsiTreeUtil;
29 import com.intellij.psi.util.TypeConversionUtil;
30 import com.intellij.refactoring.HelpID;
31 import com.intellij.refactoring.JavaRefactoringSettings;
32 import com.intellij.refactoring.RefactoringBundle;
33 import com.intellij.refactoring.listeners.RefactoringElementListener;
34 import com.intellij.refactoring.util.ConflictsUtil;
35 import com.intellij.refactoring.util.MoveRenameUsageInfo;
36 import com.intellij.refactoring.util.RefactoringUtil;
37 import com.intellij.usageView.UsageInfo;
38 import com.intellij.util.IncorrectOperationException;
39 import com.intellij.util.Processor;
40 import com.intellij.util.containers.HashSet;
41 import com.intellij.util.containers.MultiMap;
42 import org.jetbrains.annotations.NonNls;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
46 import java.util.*;
48 public class RenameJavaMethodProcessor extends RenameJavaMemberProcessor {
49 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.rename.RenameJavaMethodProcessor");
51 public boolean canProcessElement(final PsiElement element) {
52 return element instanceof PsiMethod;
55 public void renameElement(final PsiElement psiElement,
56 final String newName,
57 final UsageInfo[] usages, final RefactoringElementListener listener) throws IncorrectOperationException {
58 PsiMethod method = (PsiMethod) psiElement;
59 Set<PsiMethod> methodAndOverriders = new HashSet<PsiMethod>();
60 Set<PsiClass> containingClasses = new HashSet<PsiClass>();
61 List<PsiElement> renamedReferences = new ArrayList<PsiElement>();
62 List<MemberHidesOuterMemberUsageInfo> outerHides = new ArrayList<MemberHidesOuterMemberUsageInfo>();
63 List<MemberHidesStaticImportUsageInfo> staticImportHides = new ArrayList<MemberHidesStaticImportUsageInfo>();
65 methodAndOverriders.add(method);
66 containingClasses.add(method.getContainingClass());
68 findCollisionsAgainstNewName(method, newName, staticImportHides);
70 // do actual rename of overriding/implementing methods and of references to all them
71 for (UsageInfo usage : usages) {
72 PsiElement element = usage.getElement();
73 if (element == null) continue;
75 if (usage instanceof MemberHidesOuterMemberUsageInfo) {
76 PsiJavaCodeReferenceElement collidingRef = (PsiJavaCodeReferenceElement)element;
77 PsiMethod resolved = (PsiMethod)collidingRef.resolve();
78 outerHides.add(new MemberHidesOuterMemberUsageInfo(element, resolved));
80 else if (!(element instanceof PsiMethod)) {
81 final PsiReference ref;
82 if (usage instanceof MoveRenameUsageInfo) {
83 ref = usage.getReference();
85 else {
86 ref = element.getReference();
88 if (ref != null) {
89 renamedReferences.add(ref.handleElementRename(newName));
92 else {
93 PsiMethod overrider = (PsiMethod)element;
94 methodAndOverriders.add(overrider);
95 containingClasses.add(overrider.getContainingClass());
99 // do actual rename of method
100 method.setName(newName);
101 for (UsageInfo usage : usages) {
102 PsiElement element = usage.getElement();
103 if (element instanceof PsiMethod) {
104 ((PsiMethod)element).setName(newName);
107 listener.elementRenamed(method);
109 for (PsiElement element: renamedReferences) {
110 fixNameCollisionsWithInnerClassMethod(element, newName, methodAndOverriders, containingClasses,
111 method.hasModifierProperty(PsiModifier.STATIC));
113 qualifyOuterMemberReferences(outerHides);
114 qualifyStaticImportReferences(staticImportHides);
117 private static void fixNameCollisionsWithInnerClassMethod(final PsiElement element, final String newName,
118 final Set<PsiMethod> methodAndOverriders, final Set<PsiClass> containingClasses,
119 final boolean isStatic) throws IncorrectOperationException {
120 if (!(element instanceof PsiReferenceExpression)) return;
121 PsiElement elem = ((PsiReferenceExpression)element).resolve();
123 if (elem instanceof PsiMethod) {
124 PsiMethod actualMethod = (PsiMethod) elem;
125 if (!methodAndOverriders.contains(actualMethod)) {
126 PsiClass outerClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
127 while (outerClass != null) {
128 if (containingClasses.contains(outerClass)) {
129 qualifyMember(element, newName, outerClass, isStatic);
130 break;
132 outerClass = outerClass.getContainingClass();
138 @NotNull
139 public Collection<PsiReference> findReferences(final PsiElement element) {
140 GlobalSearchScope projectScope = GlobalSearchScope.projectScope(element.getProject());
141 return MethodReferencesSearch.search((PsiMethod)element, projectScope, true).findAll();
144 public void findCollisions(final PsiElement element, final String newName, final Map<? extends PsiElement, String> allRenames,
145 final List<UsageInfo> result) {
146 final PsiMethod methodToRename = (PsiMethod)element;
147 findSubmemberHidesMemberCollisions(methodToRename, newName, result);
148 findMemberHidesOuterMemberCollisions((PsiMethod) element, newName, result);
151 public void findExistingNameConflicts(final PsiElement element, final String newName, final MultiMap<PsiElement, String> conflicts) {
152 if (element instanceof PsiCompiledElement) return;
153 PsiMethod refactoredMethod = (PsiMethod)element;
154 if (newName.equals(refactoredMethod.getName())) return;
155 final PsiMethod prototype = (PsiMethod)refactoredMethod.copy();
156 try {
157 prototype.setName(newName);
159 catch (IncorrectOperationException e) {
160 LOG.error(e);
161 return;
164 ConflictsUtil.checkMethodConflicts(
165 refactoredMethod.getContainingClass(),
166 refactoredMethod,
167 prototype,
168 conflicts);
171 public void prepareRenaming(final PsiElement element, final String newName, final Map<PsiElement, String> allRenames) {
172 final PsiMethod method = (PsiMethod) element;
173 OverridingMethodsSearch.search(method, true).forEach(new Processor<PsiMethod>() {
174 public boolean process(PsiMethod overrider) {
175 final String overriderName = overrider.getName();
176 final String baseName = method.getName();
177 final String newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName);
178 if (newOverriderName != null) {
179 allRenames.put(overrider, newOverriderName);
181 return true;
186 @NonNls
187 public String getHelpID(final PsiElement element) {
188 return HelpID.RENAME_METHOD;
191 public boolean isToSearchInComments(final PsiElement psiElement) {
192 return JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD;
195 public void setToSearchInComments(final PsiElement element, final boolean enabled) {
196 JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled;
199 @Nullable
200 public PsiElement substituteElementToRename(PsiElement element, Editor editor) {
201 PsiMethod psiMethod = (PsiMethod)element;
202 if (psiMethod.isConstructor()) {
203 PsiClass containingClass = psiMethod.getContainingClass();
204 if (containingClass == null) return null;
205 element = containingClass;
206 if (!PsiElementRenameHandler.canRename(element.getProject(), editor, element)) {
207 return null;
209 return element;
211 return SuperMethodWarningUtil.checkSuperMethod(psiMethod, RefactoringBundle.message("to.rename"));
214 private static void findSubmemberHidesMemberCollisions(final PsiMethod method, final String newName, final List<UsageInfo> result) {
215 final PsiClass containingClass = method.getContainingClass();
216 if (containingClass == null) return;
217 if (method.hasModifierProperty(PsiModifier.PRIVATE)) return;
218 Collection<PsiClass> inheritors = ClassInheritorsSearch.search(containingClass, containingClass.getUseScope(), true).findAll();
220 MethodSignature oldSignature = method.getSignature(PsiSubstitutor.EMPTY);
221 MethodSignature newSignature = MethodSignatureUtil.createMethodSignature(newName, oldSignature.getParameterTypes(),
222 oldSignature.getTypeParameters(),
223 oldSignature.getSubstitutor());
224 for (PsiClass inheritor : inheritors) {
225 PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY);
226 final PsiMethod[] methodsByName = inheritor.findMethodsByName(newName, false);
227 for (PsiMethod conflictingMethod : methodsByName) {
228 if (newSignature.equals(conflictingMethod.getSignature(superSubstitutor))) {
229 result.add(new SubmemberHidesMemberUsageInfo(conflictingMethod, method));
230 break;