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
.codeInsight
.ChangeContextUtil
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.project
.Project
;
21 import com
.intellij
.openapi
.util
.Comparing
;
22 import com
.intellij
.openapi
.util
.Pair
;
23 import com
.intellij
.psi
.*;
24 import com
.intellij
.psi
.search
.GlobalSearchScope
;
25 import com
.intellij
.psi
.search
.LocalSearchScope
;
26 import com
.intellij
.psi
.search
.searches
.ClassInheritorsSearch
;
27 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
28 import com
.intellij
.psi
.util
.ClassUtil
;
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
.MoveRenameUsageInfo
;
34 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
35 import com
.intellij
.usageView
.UsageInfo
;
36 import com
.intellij
.util
.IncorrectOperationException
;
37 import com
.intellij
.util
.containers
.MultiMap
;
38 import org
.jetbrains
.annotations
.NonNls
;
39 import org
.jetbrains
.annotations
.NotNull
;
40 import org
.jetbrains
.annotations
.Nullable
;
43 import java
.util
.regex
.Pattern
;
48 public class RenameJavaClassProcessor
extends RenamePsiElementProcessor
{
49 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.rename.RenameJavaClassProcessor");
51 public boolean canProcessElement(final PsiElement element
) {
52 return element
instanceof PsiClass
;
55 public void renameElement(final PsiElement element
,
57 final UsageInfo
[] usages
, final RefactoringElementListener listener
) throws IncorrectOperationException
{
58 PsiClass aClass
= (PsiClass
) element
;
59 ArrayList
<UsageInfo
> postponedCollisions
= new ArrayList
<UsageInfo
>();
60 // rename all references
61 for (final UsageInfo usage
: usages
) {
62 if (usage
instanceof ResolvableCollisionUsageInfo
) {
63 if (usage
instanceof CollidingClassImportUsageInfo
) {
64 ((CollidingClassImportUsageInfo
)usage
).getImportStatement().delete();
67 postponedCollisions
.add(usage
);
73 ChangeContextUtil
.encodeContextInfo(aClass
, true);
74 aClass
.setName(newName
);
76 for (UsageInfo usage
: usages
) {
77 if (!(usage
instanceof ResolvableCollisionUsageInfo
)) {
78 final PsiReference ref
= usage
.getReference();
79 if (ref
== null) continue;
81 ref
.bindToElement(aClass
);
83 catch (IncorrectOperationException e
) {//fall back to old scheme
84 ref
.handleElementRename(newName
);
89 ChangeContextUtil
.decodeContextInfo(aClass
, null, null); //to make refs to other classes from this one resolve to their old referent
92 for (UsageInfo postponedCollision
: postponedCollisions
) {
93 ClassHidesImportedClassUsageInfo collision
= (ClassHidesImportedClassUsageInfo
) postponedCollision
;
94 collision
.resolveCollision();
97 listener
.elementRenamed(aClass
);
101 public Pair
<String
, String
> getTextOccurrenceSearchStrings(@NotNull final PsiElement element
, @NotNull final String newName
) {
102 if (element
instanceof PsiClass
) {
103 final PsiClass aClass
= (PsiClass
)element
;
104 if (aClass
.getParent() instanceof PsiClass
) {
105 final String dollaredStringToSearch
= ClassUtil
.getJVMClassName(aClass
);
106 final String dollaredStringToReplace
= dollaredStringToSearch
== null ?
null : RefactoringUtil
.getNewInnerClassName(aClass
, dollaredStringToSearch
, newName
);
107 if (dollaredStringToReplace
!= null) {
108 return new Pair
<String
, String
>(dollaredStringToSearch
, dollaredStringToReplace
);
115 public String
getQualifiedNameAfterRename(final PsiElement element
, final String newName
, final boolean nonJava
) {
117 final PsiClass aClass
= (PsiClass
)element
;
118 return RenameUtil
.getQualifiedNameAfterRename(aClass
.getQualifiedName(), newName
);
125 public void prepareRenaming(final PsiElement element
, final String newName
, final Map
<PsiElement
, String
> allRenames
) {
126 final PsiMethod
[] constructors
= ((PsiClass
) element
).getConstructors();
127 for (PsiMethod constructor
: constructors
) {
128 allRenames
.put(constructor
, newName
);
132 public void findCollisions(final PsiElement element
, final String newName
, final Map
<?
extends PsiElement
, String
> allRenames
, final List
<UsageInfo
> result
) {
133 final PsiClass aClass
= (PsiClass
)element
;
134 final ClassCollisionsDetector classCollisionsDetector
= new ClassCollisionsDetector(aClass
);
135 Collection
<UsageInfo
> initialResults
= new ArrayList
<UsageInfo
>(result
);
136 for(UsageInfo usageInfo
: initialResults
) {
137 if (usageInfo
instanceof MoveRenameUsageInfo
) {
138 classCollisionsDetector
.addClassCollisions(usageInfo
.getElement(), newName
, result
);
141 findSubmemberHidesMemberCollisions(aClass
, newName
, result
);
144 public static void findSubmemberHidesMemberCollisions(final PsiClass aClass
, final String newName
, final List
<UsageInfo
> result
) {
145 if (aClass
.getParent() instanceof PsiClass
) {
146 PsiClass parent
= (PsiClass
)aClass
.getParent();
147 Collection
<PsiClass
> inheritors
= ClassInheritorsSearch
.search(parent
, parent
.getUseScope(), true).findAll();
148 for (PsiClass inheritor
: inheritors
) {
149 PsiClass
[] inners
= inheritor
.getInnerClasses();
150 for (PsiClass inner
: inners
) {
151 if (newName
.equals(inner
.getName())) {
152 result
.add(new SubmemberHidesMemberUsageInfo(inner
, aClass
));
159 private static class ClassCollisionsDetector
{
160 final HashSet
<PsiFile
> myProcessedFiles
= new HashSet
<PsiFile
>();
161 final PsiClass myRenamedClass
;
162 private final String myRenamedClassQualifiedName
;
164 public ClassCollisionsDetector(PsiClass renamedClass
) {
165 myRenamedClass
= renamedClass
;
166 myRenamedClassQualifiedName
= myRenamedClass
.getQualifiedName();
169 public void addClassCollisions(PsiElement referenceElement
, String newName
, List
<UsageInfo
> results
) {
170 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(referenceElement
.getProject()).getResolveHelper();
171 final PsiClass aClass
= resolveHelper
.resolveReferencedClass(newName
, referenceElement
);
172 if (aClass
== null) return;
173 final PsiFile containingFile
= referenceElement
.getContainingFile();
174 final String text
= referenceElement
.getText();
175 if (Comparing
.equal(myRenamedClassQualifiedName
, removeSpaces(text
))) return;
176 if (myProcessedFiles
.contains(containingFile
)) return;
177 for (PsiReference reference
: ReferencesSearch
.search(aClass
, new LocalSearchScope(containingFile
))) {
178 final PsiElement collisionReferenceElement
= reference
.getElement();
179 if (collisionReferenceElement
instanceof PsiJavaCodeReferenceElement
) {
180 final PsiElement parent
= collisionReferenceElement
.getParent();
181 if (parent
instanceof PsiImportStatement
) {
182 results
.add(new CollidingClassImportUsageInfo((PsiImportStatement
)parent
, myRenamedClass
));
185 if (aClass
.getQualifiedName() != null) {
186 results
.add(new ClassHidesImportedClassUsageInfo((PsiJavaCodeReferenceElement
)collisionReferenceElement
,
187 myRenamedClass
, aClass
));
190 results
.add(new ClassHidesUnqualifiableClassUsageInfo((PsiJavaCodeReferenceElement
)collisionReferenceElement
,
191 myRenamedClass
, aClass
));
196 myProcessedFiles
.add(containingFile
);
200 @NonNls private static final Pattern WHITE_SPACE_PATTERN
= Pattern
.compile("\\s");
202 private static String
removeSpaces(String s
) {
203 return WHITE_SPACE_PATTERN
.matcher(s
).replaceAll("");
206 public void findExistingNameConflicts(final PsiElement element
, final String newName
, final MultiMap
<PsiElement
,String
> conflicts
) {
207 if (element
instanceof PsiCompiledElement
) return;
208 final PsiClass aClass
= (PsiClass
)element
;
209 if (newName
.equals(aClass
.getName())) return;
210 final PsiClass containingClass
= aClass
.getContainingClass();
211 if (containingClass
!= null) { // innerClass
212 PsiClass
[] innerClasses
= containingClass
.getInnerClasses();
213 for (PsiClass innerClass
: innerClasses
) {
214 if (newName
.equals(innerClass
.getName())) {
215 conflicts
.putValue(innerClass
, RefactoringBundle
.message("inner.class.0.is.already.defined.in.class.1", newName
, containingClass
.getQualifiedName()));
221 final String qualifiedNameAfterRename
= RenameUtil
.getQualifiedNameAfterRename(aClass
.getQualifiedName(), newName
);
222 Project project
= element
.getProject();
223 final PsiClass conflictingClass
=
224 JavaPsiFacade
.getInstance(project
).findClass(qualifiedNameAfterRename
, GlobalSearchScope
.allScope(project
));
225 if (conflictingClass
!= null) {
226 conflicts
.putValue(conflictingClass
, RefactoringBundle
.message("class.0.already.exists", qualifiedNameAfterRename
));
233 public String
getHelpID(final PsiElement element
) {
234 return HelpID
.RENAME_CLASS
;
237 public boolean isToSearchInComments(final PsiElement psiElement
) {
238 return JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
;
241 public void setToSearchInComments(final PsiElement element
, final boolean enabled
) {
242 JavaRefactoringSettings
.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
= enabled
;
245 public boolean isToSearchForTextOccurrences(final PsiElement element
) {
246 return JavaRefactoringSettings
.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS
;
249 public void setToSearchForTextOccurrences(final PsiElement element
, final boolean enabled
) {
250 JavaRefactoringSettings
.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS
= enabled
;