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.lang.StdLanguages;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.ui.Messages;
23 import com.intellij.psi.*;
24 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
25 import com.intellij.psi.codeStyle.VariableKind;
26 import com.intellij.psi.search.searches.ClassInheritorsSearch;
27 import com.intellij.psi.search.searches.OverridingMethodsSearch;
28 import com.intellij.psi.util.PropertyUtil;
29 import com.intellij.psi.util.PsiUtil;
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.RefactoringMessageUtil;
37 import com.intellij.refactoring.util.RefactoringUtil;
38 import com.intellij.usageView.UsageInfo;
39 import com.intellij.util.IncorrectOperationException;
40 import com.intellij.util.Processor;
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.ArrayList;
47 import java.util.Collection;
48 import java.util.List;
51 public class RenameJavaVariableProcessor extends RenameJavaMemberProcessor {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.rename.RenameJavaVariableProcessor");
54 public boolean canProcessElement(@NotNull final PsiElement element) {
55 return element instanceof PsiVariable;
58 public void renameElement(final PsiElement psiElement,
60 final UsageInfo[] usages, final RefactoringElementListener listener) throws IncorrectOperationException {
61 PsiVariable variable = (PsiVariable) psiElement;
62 List<MemberHidesOuterMemberUsageInfo> outerHides = new ArrayList<MemberHidesOuterMemberUsageInfo>();
63 List<MemberHidesStaticImportUsageInfo> staticImportHides = new ArrayList<MemberHidesStaticImportUsageInfo>();
65 List<PsiElement> occurrencesToCheckForConflict = new ArrayList<PsiElement>();
66 // rename all references
67 for (UsageInfo usage : usages) {
68 final PsiElement element = usage.getElement();
69 if (element == null) continue;
71 if (usage instanceof MemberHidesStaticImportUsageInfo) {
72 staticImportHides.add((MemberHidesStaticImportUsageInfo)usage);
73 } else if (usage instanceof LocalHidesFieldUsageInfo) {
74 PsiJavaCodeReferenceElement collidingRef = (PsiJavaCodeReferenceElement)element;
75 PsiElement resolved = collidingRef.resolve();
77 if (resolved instanceof PsiField) {
78 qualifyMember((PsiField)resolved, collidingRef, newName);
84 else if (usage instanceof MemberHidesOuterMemberUsageInfo) {
85 PsiJavaCodeReferenceElement collidingRef = (PsiJavaCodeReferenceElement)element;
86 PsiField resolved = (PsiField)collidingRef.resolve();
87 outerHides.add(new MemberHidesOuterMemberUsageInfo(element, resolved));
90 final PsiReference ref;
91 if (usage instanceof MoveRenameUsageInfo) {
92 ref = usage.getReference();
95 ref = element.getReference();
98 PsiElement newElem = ref.handleElementRename(newName);
99 if (variable instanceof PsiField) {
100 occurrencesToCheckForConflict.add(newElem);
106 variable.setName(newName);
107 listener.elementRenamed(variable);
109 if (variable instanceof PsiField) {
110 for (PsiElement occurrence : occurrencesToCheckForConflict) {
111 fixPossibleNameCollisionsForFieldRenaming((PsiField) variable, newName, occurrence);
115 qualifyOuterMemberReferences(outerHides);
116 qualifyStaticImportReferences(staticImportHides);
119 private static void fixPossibleNameCollisionsForFieldRenaming(PsiField field, String newName, PsiElement replacedOccurence) throws IncorrectOperationException {
120 if (!(replacedOccurence instanceof PsiReferenceExpression)) return;
121 PsiElement elem = ((PsiReferenceExpression)replacedOccurence).resolve();
123 if (elem == null || elem == field) {
124 // If reference is unresolved, then field is not hidden by anyone...
128 if (elem instanceof PsiLocalVariable || elem instanceof PsiParameter || (elem instanceof PsiField && elem != replacedOccurence)) {
129 qualifyMember(field, replacedOccurence, newName);
133 public void prepareRenaming(final PsiElement element, final String newName, final Map<PsiElement, String> allRenames) {
134 if (element instanceof PsiField && StdLanguages.JAVA.equals(element.getLanguage())) {
135 prepareFieldRenaming((PsiField)element, newName, allRenames);
139 private static void prepareFieldRenaming(PsiField field, String newName, final Map<PsiElement, String> allRenames) {
140 // search for getters/setters
141 PsiClass aClass = field.getContainingClass();
143 Project project = field.getProject();
144 final JavaCodeStyleManager manager = JavaCodeStyleManager.getInstance(project);
146 final String propertyName = manager.variableNameToPropertyName(field.getName(), VariableKind.FIELD);
147 String newPropertyName = manager.variableNameToPropertyName(newName, VariableKind.FIELD);
149 boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
150 PsiMethod getter = PropertyUtil.findPropertyGetter(aClass, propertyName, isStatic, false);
151 PsiMethod setter = PropertyUtil.findPropertySetter(aClass, propertyName, isStatic, false);
153 boolean shouldRenameSetterParameter = false;
155 if (setter != null) {
156 String parameterName = manager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER);
157 PsiParameter setterParameter = setter.getParameterList().getParameters()[0];
158 shouldRenameSetterParameter = parameterName.equals(setterParameter.getName());
161 String newGetterName = "";
163 if (getter != null) {
164 String getterId = getter.getName();
165 newGetterName = PropertyUtil.suggestGetterName(newPropertyName, field.getType(), getterId);
166 if (newGetterName.equals(getterId)) {
168 newGetterName = null;
170 for (PsiMethod method : getter.findDeepestSuperMethods()) {
171 if (method instanceof PsiCompiledElement) {
179 String newSetterName = "";
180 if (setter != null) {
181 newSetterName = PropertyUtil.suggestSetterName(newPropertyName);
182 final String newSetterParameterName = manager.propertyNameToVariableName(newPropertyName, VariableKind.PARAMETER);
183 if (newSetterName.equals(setter.getName())) {
185 newSetterName = null;
186 shouldRenameSetterParameter = false;
188 else if (newSetterParameterName.equals(setter.getParameterList().getParameters()[0].getName())) {
189 shouldRenameSetterParameter = false;
191 for (PsiMethod method : setter.findDeepestSuperMethods()) {
192 if (method instanceof PsiCompiledElement) {
194 shouldRenameSetterParameter = false;
201 if ((getter != null || setter != null) && askToRenameAccesors(getter, setter, newName, project)) {
204 shouldRenameSetterParameter = false;
207 if (getter != null) {
208 addOverriddenAndImplemented(getter, newGetterName, allRenames);
211 if (setter != null) {
212 addOverriddenAndImplemented(setter, newSetterName, allRenames);
215 if (shouldRenameSetterParameter) {
216 PsiParameter parameter = setter.getParameterList().getParameters()[0];
217 allRenames.put(parameter, manager.propertyNameToVariableName(newPropertyName, VariableKind.PARAMETER));
221 private static boolean askToRenameAccesors(PsiMethod getter, PsiMethod setter, String newName, final Project project) {
222 if (ApplicationManager.getApplication().isUnitTestMode()) return false;
223 String text = RefactoringMessageUtil.getGetterSetterMessage(newName, RefactoringBundle.message("rename.title"), getter, setter);
224 return Messages.showYesNoDialog(project, text, RefactoringBundle.message("rename.title"), Messages.getQuestionIcon()) != 0;
227 private static void addOverriddenAndImplemented(PsiMethod methodPrototype, final String newName, final Map<PsiElement, String> allRenames) {
228 allRenames.put(methodPrototype, newName);
229 for (PsiMethod method : methodPrototype.findDeepestSuperMethods()) {
230 OverridingMethodsSearch.search(method).forEach(new Processor<PsiMethod>() {
231 public boolean process(PsiMethod psiMethod) {
232 RenameProcessor.assertNonCompileElement(psiMethod);
233 allRenames.put(psiMethod, newName);
237 allRenames.put(method, newName);
241 public void findCollisions(final PsiElement element, final String newName, final Map<? extends PsiElement, String> allRenames,
242 final List<UsageInfo> result) {
243 if (element instanceof PsiField) {
244 PsiField field = (PsiField) element;
245 findMemberHidesOuterMemberCollisions(field, newName, result);
246 findSubmemberHidesFieldCollisions(field, newName, result);
247 findCollisionsAgainstNewName(field, newName, result);
249 else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
250 JavaUnresolvableLocalCollisionDetector.findCollisions(element, newName, result);
251 findLocalHidesFieldCollisions(element, newName, allRenames, result);
256 public void findExistingNameConflicts(PsiElement element, String newName, MultiMap<PsiElement, String> conflicts) {
257 if (element instanceof PsiCompiledElement) return;
258 if (element instanceof PsiField) {
259 PsiField refactoredField = (PsiField)element;
260 if (newName.equals(refactoredField.getName())) return;
261 ConflictsUtil.checkFieldConflicts(
262 refactoredField.getContainingClass(),
271 public String getHelpID(final PsiElement element) {
272 if (element instanceof PsiField){
273 return HelpID.RENAME_FIELD;
275 else if (element instanceof PsiLocalVariable){
276 return HelpID.RENAME_VARIABLE;
278 else if (element instanceof PsiParameter){
279 return HelpID.RENAME_PARAMETER;
284 public boolean isToSearchInComments(final PsiElement element) {
285 if (element instanceof PsiField){
286 return JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD;
288 return JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE;
291 public void setToSearchInComments(final PsiElement element, final boolean enabled) {
292 if (element instanceof PsiField){
293 JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled;
295 JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled;
298 public boolean isToSearchForTextOccurrences(final PsiElement element) {
299 if (element instanceof PsiField) {
300 return JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD;
302 return JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_VARIABLE;
305 public void setToSearchForTextOccurrences(final PsiElement element, final boolean enabled) {
306 if (element instanceof PsiField) {
307 JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled;
309 JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_VARIABLE = enabled;
312 private static void findSubmemberHidesFieldCollisions(final PsiField field, final String newName, final List<UsageInfo> result) {
313 if (field.getContainingClass() == null) return;
314 if (field.hasModifierProperty(PsiModifier.PRIVATE)) return;
315 final PsiClass containingClass = field.getContainingClass();
316 Collection<PsiClass> inheritors = ClassInheritorsSearch.search(containingClass, true).findAll();
317 for (PsiClass inheritor : inheritors) {
318 PsiField conflictingField = inheritor.findFieldByName(newName, false);
319 if (conflictingField != null) {
320 result.add(new SubmemberHidesMemberUsageInfo(conflictingField, field));
325 private static void findLocalHidesFieldCollisions(final PsiElement element, final String newName, final Map<? extends PsiElement, String> allRenames, final List<UsageInfo> result) {
326 if (!(element instanceof PsiLocalVariable) && !(element instanceof PsiParameter)) return;
328 PsiClass toplevel = PsiUtil.getTopLevelClass(element);
329 if (toplevel == null) return;
331 PsiElement scopeElement;
332 if (element instanceof PsiLocalVariable) {
333 scopeElement = RefactoringUtil.getVariableScope((PsiLocalVariable)element);
336 scopeElement = ((PsiParameter) element).getDeclarationScope();
339 LOG.assertTrue(scopeElement != null);
340 scopeElement.accept(new JavaRecursiveElementWalkingVisitor() {
341 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
342 super.visitReferenceExpression(expression);
343 if (!expression.isQualified()) {
344 PsiElement resolved = expression.resolve();
345 if (resolved instanceof PsiField) {
346 final PsiField field = (PsiField)resolved;
347 String fieldNewName = allRenames.containsKey(field) ? allRenames.get(field) : field.getName();
348 if (newName.equals(fieldNewName)) {
349 result.add(new LocalHidesFieldUsageInfo(expression, element));