967825a50ae10cf3940700108b717eafb953f189
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / rename / RenameJavaVariableProcessor.java
blob967825a50ae10cf3940700108b717eafb953f189
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.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;
47 import java.util.Map;
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,
57 final String newName,
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);
82 else {
83 // do nothing
86 else if (usage instanceof MemberHidesOuterMemberUsageInfo) {
87 PsiJavaCodeReferenceElement collidingRef = (PsiJavaCodeReferenceElement)element;
88 PsiField resolved = (PsiField)collidingRef.resolve();
89 outerHides.add(new MemberHidesOuterMemberUsageInfo(element, resolved));
91 else {
92 final PsiReference ref;
93 if (usage instanceof MoveRenameUsageInfo) {
94 ref = usage.getReference();
96 else {
97 ref = element.getReference();
99 if (ref != null) {
100 PsiElement newElem = ref.handleElementRename(newName);
101 if (variable instanceof PsiField) {
102 occurrencesToCheckForConflict.add(newElem);
107 // do actual rename
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...
127 return;
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)) {
169 getter = null;
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())) {
179 setter = null;
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)) {
189 getter = null;
190 setter = null;
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);
220 return true;
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);
241 @Override
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(),
249 newName,
250 conflicts
255 @Nullable
256 @NonNls
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;
267 return null;
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);
307 else { // Parameter
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));