update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / rename / RenameJavaClassProcessor.java
blobf3a71534b293ba5df3c8d5f763e3eb4de5f38c7f
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.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;
42 import java.util.*;
43 import java.util.regex.Pattern;
45 /**
46 * @author yole
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,
56 final String newName,
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();
66 else {
67 postponedCollisions.add(usage);
72 // do actual rename
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;
80 try {
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
91 // resolve collisions
92 for (UsageInfo postponedCollision : postponedCollisions) {
93 ClassHidesImportedClassUsageInfo collision = (ClassHidesImportedClassUsageInfo) postponedCollision;
94 collision.resolveCollision();
97 listener.elementRenamed(aClass);
100 @Nullable
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);
112 return null;
115 public String getQualifiedNameAfterRename(final PsiElement element, final String newName, final boolean nonJava) {
116 if (nonJava) {
117 final PsiClass aClass = (PsiClass)element;
118 return RenameUtil.getQualifiedNameAfterRename(aClass.getQualifiedName(), newName);
120 else {
121 return 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));
184 else {
185 if (aClass.getQualifiedName() != null) {
186 results.add(new ClassHidesImportedClassUsageInfo((PsiJavaCodeReferenceElement)collisionReferenceElement,
187 myRenamedClass, aClass));
189 else {
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()));
216 break;
220 else {
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));
231 @Nullable
232 @NonNls
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;