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
.openapi
.application
.ApplicationManager
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.project
.Project
;
21 import com
.intellij
.openapi
.ui
.Messages
;
22 import com
.intellij
.psi
.*;
23 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
24 import com
.intellij
.psi
.codeStyle
.VariableKind
;
25 import com
.intellij
.psi
.search
.searches
.ClassInheritorsSearch
;
26 import com
.intellij
.psi
.search
.searches
.OverridingMethodsSearch
;
27 import com
.intellij
.psi
.util
.PropertyUtil
;
28 import com
.intellij
.psi
.util
.PsiUtil
;
29 import com
.intellij
.refactoring
.HelpID
;
30 import com
.intellij
.refactoring
.JavaRefactoringSettings
;
31 import com
.intellij
.refactoring
.RefactoringBundle
;
32 import com
.intellij
.refactoring
.listeners
.RefactoringElementListener
;
33 import com
.intellij
.refactoring
.util
.ConflictsUtil
;
34 import com
.intellij
.refactoring
.util
.MoveRenameUsageInfo
;
35 import com
.intellij
.refactoring
.util
.RefactoringMessageUtil
;
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
.MultiMap
;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.Nullable
;
44 import java
.util
.ArrayList
;
45 import java
.util
.Collection
;
46 import java
.util
.List
;
49 public class RenameJavaVariableProcessor
extends RenameJavaMemberProcessor
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.rename.RenameJavaVariableProcessor");
52 public boolean canProcessElement(final PsiElement element
) {
53 return element
instanceof PsiVariable
;
56 public void renameElement(final PsiElement psiElement
,
58 final UsageInfo
[] usages
, final RefactoringElementListener listener
) throws IncorrectOperationException
{
59 PsiVariable variable
= (PsiVariable
) psiElement
;
60 List
<MemberHidesOuterMemberUsageInfo
> outerHides
= new ArrayList
<MemberHidesOuterMemberUsageInfo
>();
61 List
<MemberHidesStaticImportUsageInfo
> staticImportHides
= new ArrayList
<MemberHidesStaticImportUsageInfo
>();
63 if (variable
instanceof PsiField
) {
64 findCollisionsAgainstNewName((PsiField
) variable
, newName
, staticImportHides
);
67 List
<PsiElement
> occurrencesToCheckForConflict
= new ArrayList
<PsiElement
>();
68 // rename all references
69 for (UsageInfo usage
: usages
) {
70 final PsiElement element
= usage
.getElement();
71 if (element
== null) continue;
73 if (usage
instanceof MemberHidesStaticImportUsageInfo
) {
74 staticImportHides
.add((MemberHidesStaticImportUsageInfo
)usage
);
75 } else if (usage
instanceof LocalHidesFieldUsageInfo
) {
76 PsiJavaCodeReferenceElement collidingRef
= (PsiJavaCodeReferenceElement
)element
;
77 PsiElement resolved
= collidingRef
.resolve();
79 if (resolved
instanceof PsiField
) {
80 qualifyMember((PsiField
)resolved
, collidingRef
, newName
);
86 else if (usage
instanceof MemberHidesOuterMemberUsageInfo
) {
87 PsiJavaCodeReferenceElement collidingRef
= (PsiJavaCodeReferenceElement
)element
;
88 PsiField resolved
= (PsiField
)collidingRef
.resolve();
89 outerHides
.add(new MemberHidesOuterMemberUsageInfo(element
, resolved
));
92 final PsiReference ref
;
93 if (usage
instanceof MoveRenameUsageInfo
) {
94 ref
= usage
.getReference();
97 ref
= element
.getReference();
100 PsiElement newElem
= ref
.handleElementRename(newName
);
101 if (variable
instanceof PsiField
) {
102 occurrencesToCheckForConflict
.add(newElem
);
108 variable
.setName(newName
);
109 listener
.elementRenamed(variable
);
111 if (variable
instanceof PsiField
) {
112 for (PsiElement occurrence
: occurrencesToCheckForConflict
) {
113 fixPossibleNameCollisionsForFieldRenaming((PsiField
) variable
, newName
, occurrence
);
117 qualifyOuterMemberReferences(outerHides
);
118 qualifyStaticImportReferences(staticImportHides
);
121 private static void fixPossibleNameCollisionsForFieldRenaming(PsiField field
, String newName
, PsiElement replacedOccurence
) throws IncorrectOperationException
{
122 if (!(replacedOccurence
instanceof PsiReferenceExpression
)) return;
123 PsiElement elem
= ((PsiReferenceExpression
)replacedOccurence
).resolve();
125 if (elem
== null || elem
== field
) {
126 // If reference is unresolved, then field is not hidden by anyone...
130 if (elem
instanceof PsiLocalVariable
|| elem
instanceof PsiParameter
|| (elem
instanceof PsiField
&& elem
!= replacedOccurence
)) {
131 qualifyMember(field
, replacedOccurence
, newName
);
135 public void prepareRenaming(final PsiElement element
, final String newName
, final Map
<PsiElement
, String
> allRenames
) {
136 if (element
instanceof PsiField
) {
137 prepareFieldRenaming((PsiField
)element
, newName
, allRenames
);
141 private static void prepareFieldRenaming(PsiField field
, String newName
, final Map
<PsiElement
, String
> allRenames
) {
142 // search for getters/setters
143 PsiClass aClass
= field
.getContainingClass();
145 Project project
= field
.getProject();
146 final JavaCodeStyleManager manager
= JavaCodeStyleManager
.getInstance(project
);
148 final String propertyName
= manager
.variableNameToPropertyName(field
.getName(), VariableKind
.FIELD
);
149 String newPropertyName
= manager
.variableNameToPropertyName(newName
, VariableKind
.FIELD
);
151 boolean isStatic
= field
.hasModifierProperty(PsiModifier
.STATIC
);
152 PsiMethod getter
= PropertyUtil
.findPropertyGetter(aClass
, propertyName
, isStatic
, false);
153 PsiMethod setter
= PropertyUtil
.findPropertySetter(aClass
, propertyName
, isStatic
, false);
155 boolean shouldRenameSetterParameter
= false;
157 if (setter
!= null) {
158 String parameterName
= manager
.propertyNameToVariableName(propertyName
, VariableKind
.PARAMETER
);
159 PsiParameter setterParameter
= setter
.getParameterList().getParameters()[0];
160 shouldRenameSetterParameter
= parameterName
.equals(setterParameter
.getName());
163 String newGetterName
= "";
165 if (getter
!= null) {
166 String getterId
= getter
.getName();
167 newGetterName
= PropertyUtil
.suggestGetterName(newPropertyName
, field
.getType(), getterId
);
168 if (newGetterName
.equals(getterId
)) {
170 newGetterName
= null;
174 String newSetterName
= "";
175 if (setter
!= null) {
176 newSetterName
= PropertyUtil
.suggestSetterName(newPropertyName
);
177 final String newSetterParameterName
= manager
.propertyNameToVariableName(newPropertyName
, VariableKind
.PARAMETER
);
178 if (newSetterName
.equals(setter
.getName())) {
180 newSetterName
= null;
181 shouldRenameSetterParameter
= false;
183 else if (newSetterParameterName
.equals(setter
.getParameterList().getParameters()[0].getName())) {
184 shouldRenameSetterParameter
= false;
188 if ((getter
!= null || setter
!= null) && askToRenameAccesors(getter
, setter
, newName
, project
)) {
191 shouldRenameSetterParameter
= false;
194 if (getter
!= null) {
195 addOverriddenAndImplemented(getter
, newGetterName
, allRenames
);
198 if (setter
!= null) {
199 addOverriddenAndImplemented(setter
, newSetterName
, allRenames
);
202 if (shouldRenameSetterParameter
) {
203 PsiParameter parameter
= setter
.getParameterList().getParameters()[0];
204 allRenames
.put(parameter
, manager
.propertyNameToVariableName(newPropertyName
, VariableKind
.PARAMETER
));
208 private static boolean askToRenameAccesors(PsiMethod getter
, PsiMethod setter
, String newName
, final Project project
) {
209 if (ApplicationManager
.getApplication().isUnitTestMode()) return false;
210 String text
= RefactoringMessageUtil
.getGetterSetterMessage(newName
, RefactoringBundle
.message("rename.title"), getter
, setter
);
211 return Messages
.showYesNoDialog(project
, text
, RefactoringBundle
.message("rename.title"), Messages
.getQuestionIcon()) != 0;
214 private static void addOverriddenAndImplemented(PsiMethod methodPrototype
, final String newName
, final Map
<PsiElement
, String
> allRenames
) {
215 allRenames
.put(methodPrototype
, newName
);
216 for (PsiMethod method
: methodPrototype
.findDeepestSuperMethods()) {
217 OverridingMethodsSearch
.search(method
).forEach(new Processor
<PsiMethod
>() {
218 public boolean process(PsiMethod psiMethod
) {
219 allRenames
.put(psiMethod
, newName
);
223 allRenames
.put(method
, newName
);
227 public void findCollisions(final PsiElement element
, final String newName
, final Map
<?
extends PsiElement
, String
> allRenames
,
228 final List
<UsageInfo
> result
) {
229 if (element
instanceof PsiField
) {
230 PsiField field
= (PsiField
) element
;
231 findMemberHidesOuterMemberCollisions(field
, newName
, result
);
232 findSubmemberHidesFieldCollisions(field
, newName
, result
);
233 findCollisionsAgainstNewName(field
, newName
, result
);
235 else if (element
instanceof PsiLocalVariable
|| element
instanceof PsiParameter
) {
236 JavaUnresolvableLocalCollisionDetector
.findCollisions(element
, newName
, result
);
237 findLocalHidesFieldCollisions(element
, newName
, allRenames
, result
);
242 public void findExistingNameConflicts(PsiElement element
, String newName
, MultiMap
<PsiElement
, String
> conflicts
) {
243 if (element
instanceof PsiCompiledElement
) return;
244 if (element
instanceof PsiField
) {
245 PsiField refactoredField
= (PsiField
)element
;
246 if (newName
.equals(refactoredField
.getName())) return;
247 ConflictsUtil
.checkFieldConflicts(
248 refactoredField
.getContainingClass(),
257 public String
getHelpID(final PsiElement element
) {
258 if (element
instanceof PsiField
){
259 return HelpID
.RENAME_FIELD
;
261 else if (element
instanceof PsiLocalVariable
){
262 return HelpID
.RENAME_VARIABLE
;
264 else if (element
instanceof PsiParameter
){
265 return HelpID
.RENAME_PARAMETER
;
270 public boolean isToSearchInComments(final PsiElement element
) {
271 if (element
instanceof PsiField
){
272 return JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
;
274 return JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE
;
277 public void setToSearchInComments(final PsiElement element
, final boolean enabled
) {
278 if (element
instanceof PsiField
){
279 JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
= enabled
;
281 JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE
= enabled
;
284 private static void findSubmemberHidesFieldCollisions(final PsiField field
, final String newName
, final List
<UsageInfo
> result
) {
285 if (field
.getContainingClass() == null) return;
286 if (field
.hasModifierProperty(PsiModifier
.PRIVATE
)) return;
287 final PsiClass containingClass
= field
.getContainingClass();
288 Collection
<PsiClass
> inheritors
= ClassInheritorsSearch
.search(containingClass
, containingClass
.getUseScope(), true).findAll();
289 for (PsiClass inheritor
: inheritors
) {
290 PsiField conflictingField
= inheritor
.findFieldByName(newName
, false);
291 if (conflictingField
!= null) {
292 result
.add(new SubmemberHidesMemberUsageInfo(conflictingField
, field
));
297 private static void findLocalHidesFieldCollisions(final PsiElement element
, final String newName
, final Map
<?
extends PsiElement
, String
> allRenames
, final List
<UsageInfo
> result
) {
298 if (!(element
instanceof PsiLocalVariable
) && !(element
instanceof PsiParameter
)) return;
300 PsiClass toplevel
= PsiUtil
.getTopLevelClass(element
);
301 if (toplevel
== null) return;
303 PsiElement scopeElement
;
304 if (element
instanceof PsiLocalVariable
) {
305 scopeElement
= RefactoringUtil
.getVariableScope((PsiLocalVariable
)element
);
308 scopeElement
= ((PsiParameter
) element
).getDeclarationScope();
311 LOG
.assertTrue(scopeElement
!= null);
312 scopeElement
.accept(new JavaRecursiveElementWalkingVisitor() {
313 @Override public void visitReferenceExpression(PsiReferenceExpression expression
) {
314 super.visitReferenceExpression(expression
);
315 if (!expression
.isQualified()) {
316 PsiElement resolved
= expression
.resolve();
317 if (resolved
instanceof PsiField
) {
318 final PsiField field
= (PsiField
)resolved
;
319 String fieldNewName
= allRenames
.containsKey(field
) ? allRenames
.get(field
) : field
.getName();
320 if (newName
.equals(fieldNewName
)) {
321 result
.add(new LocalHidesFieldUsageInfo(expression
, element
));